• 再看const成员函数


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

    再看函数重载

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

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

    void fun(int a)
    {
        cout << "int a" << endl;
    }
    
    
    void fun(const int a)
    {
        cout << "const int a" << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    答案是不构成!这种写法是错误的,编译器会报错对于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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    答案是构成函数重载😅,当我知道这个特性的时候人都麻了,上面的程序的运行成果如下图:
    在这里插入图片描述
    那么这个特性实际上又会引出另一个特性: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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    答案是肯定的,问题就处在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
    • 2
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这时你定义了一个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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述
    在这个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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    上面的代码中定义了一个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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    加上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[];
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

  • 相关阅读:
    Java中的String类
    mysql 8.0 版本innodb 内存管理锁 buffer pool mutex 变化
    【Vue】自定义指令
    举个栗子~Alteryx 技巧(3):离线激活 Alteryx Designer
    [附源码]计算机毕业设计springboot演唱会门票售卖系统
    Redis数据类型
    不科学,RocketMQ生产者在一个应用服务竟然不能向多个NameServer发送消息
    Vue 开发必须知道的 36 个技巧【近1W字】
    四川九联代工M301H hi3798 mv300 mt7668魔百和 强刷和TTL线刷(救砖)经验分享
    牛客网之SQL刷题练习——一个实用的网站
  • 原文地址:https://blog.csdn.net/qq_51086532/article/details/130836023