再看const成员函数

文章探讨了C++中的函数重载,特别是const成员函数的重载。const成员函数不能修改对象的状态,但通过mutable关键字可以允许在const成员函数中修改特定成员变量。同时,文章提到了const和非const成员函数在某些情况下可以通过类型转换实现代码复用,但需要注意这样做可能带来的安全性问题。
摘要由CSDN通过智能技术生成

关于函数重载实际上我理解不是很深入,直接导致const成员函数这块出大问题,在阅读《effective C++》后才恍然大悟,做如下学习记录

再看函数重载

关于函数重载的概念我就不多说了,这里我要讨论的是一种我们常常忽视的函数重载:

下面这两个函数构成函数重载吗?

void fun(int a)
{
    cout << "int a" << endl;
}


void fun(const int a)
{
    cout << "const int a" << endl;
}

答案是不构成!这种写法是错误的,编译器会报错对于fun函数的重定义!

那下面两个函数构成函数重载吗

void fun(int *a)
{
    cout << "int *a" << endl;
}

void fun(const int *a)
{
    cout << "const int* a" << endl;
}

int main()
{
    int a = 1;
    const int b = 2;
    fun(&a);
    fun(&b);
    return 0;
}

答案是构成函数重载😅,当我知道这个特性的时候人都麻了,上面的程序的运行成果如下图:
在这里插入图片描述
那么这个特性实际上又会引出另一个特性:const成员函数重载

作为上面两个用例的引申,请判断一下下面两个 类C 的成员函数是否构成重载?


class  C
{
public:
    void fun() 
    {
        cout << "i am not const" << endl;
    }

    void fun() const 
    {
        cout << "i am const" << endl;
    }
};

int main()
{
    C x;
    const C y;
    x.fun();
    y.fun();
}

答案是肯定的,问题就处在C++类的this指针上,const成员函数的const修饰的是被类成员函数的参数列表省略的this指针,而非constthis指针就和const this指针就构成了重载。这种重载是相当隐晦,很长时间我都没有理解明白其中原理。

const成员函数

接下来我们将对const成员函数的特性进行一系列的讨论
众所周知const成员函数是:const对象才能调用的函数,非const类对象只能调用非const类对象
而const 代表的是不被修改——换句话说就是const修饰对象会被保护?

首先我们先复习一下指针的const特性,下面两种定义方式的区别是什么?

const int* a = new int;
int* const a = new int;
  1. const修饰的是指针变量a所存储的地址所对应的内存
  2. const修饰的是指针变量a

保安(const)能保护所有人(类成员)吗?


struct A
{
    //真的能保护成员变量吗
    void func1() const 
    {
        a = 10;  //错误的 ,不能修改
        cout << a << endl;
    }
    const int* func2()const   //注意返回值类型
    {
        return &a;
    }
    const int& func3()const   //注意返回值类型
    {
        return a;
    }
    int a=1;
};

int main()
{
	const A a;
}

这时你定义了一个const对象a,他在调用成员函数时那个隐藏的this指针显式的写出来为const A* this,这里的const修饰的指针变量存储地址所对应的内存,所以你无法修改对象a里面任何的字节!

但是我们可以看一下下面的情况

class A
{
public:
    A()
    {
        p = new char[100];
        strcpy(p, "hello world");
    }
    void func() const
    {
        p[5] = '$';
    }
    char* p;
};

int main()
{
    const A a;
    a.func();
    cout << a.p << endl;
}

在这里插入图片描述
在这个Demo中我们定义了一个类A的常量对象,并在常量成员函数func中对成员函数进行修改,结果就是字符串的内容被修改成功了。常量对象的成员变量被修改了,逻辑上违背了const的定义,所以这时一个很大的坑,但是语法上是没有问题的,解释如下👇
在这里插入图片描述
虽然对象a是一个const对象,但是只能代表指针变量这个变量的值是不变的,并不能保证指针变量所指向内存的不变。所以字符串“hello world”还是可以修改的。
这样就会让我们在编写代码的时候,逻辑上认为自己定义了一个“const的对象“,但是可能在使用时对数据发生了改变。

所以认为const能保护所有的成员对象的想法其实并不靠谱

mutable修饰类成员

对于某些情况我们想要修改部分const对象的成员变量值,但是const这个属性是对所有成员变量所施加的,这样会导致一些很麻烦的问题

在一个类A中我们希望部分一个const对象的a和b可以修改,但是c不能修改

class A
{
public:
    void func() const
    {
        a = 10;  //错误的行为
        b = 20;  //错误的行为
    }
    int a;
    int b;
    int c;
};
int main()
{
    const A a;
    a.func();
}

上面的代码中定义了一个const对象,这个const属性也继承到了三个成员变量身上,所以func中修改a和b的值是非法的。
如果我们想要部分的const成员变量可以修改,可以加上mutable 关键字来修饰。

class A
{
public:
    void func() const
    {
        a = 10;
        b = 20;
    }
    mutable int a;
    mutable int b;
    int c;
};
int main()
{
    const A a;
    a.func();
}

加上mutable之后,上面的代码就正确了

const/非const成员函数的复用

对于const和非const成员函数两者的功能是类似的,在一定情况下是可以代码复用的,示例如下:
我们在模拟实现stl的string 类型时对于const char& operator[](int pos) constchar& operator[](int pos)两个函数就可以使用强制类型转化进行代码复用。

class String
{
    const char& operator[](int pos) const 
    {
        //省略边界检查等一系列操作
        return p[pos];
    }

    char& operator[](int pos)
    {
        return const_cast<char&>(static_cast<const String>(*this)[pos]);
    }
    char p[];
};

但是注意你可以用const成员函数去复用非const成员函数,但是反过来的复用(即用非const成员函数去复用const成员函数)就是一种非常不安全的做法!

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值