C++成员函数形参列表后的const

一 问题

有时候我们在阅读一些源码时,发现有些类的成员函数的形参列表后面有个const,但是使用的时候感觉跟那些没有const的成员函数没有啥区别,举个简单栗子如下,

#include <iostream>

using namespace std;


class TestClass
{
public:
    TestClass() : val(100) {}
    virtual ~TestClass() {}


    int getVal_v1()
    {
        return val;
    }

    int getVal_v2() const
    {
        return val;
    }

private:
    int val;
};


int main()
{
    TestClass tc;

    int a = tc.getVal_v1();
    int b = tc.getVal_v2();

    cout << "getVal_v1: " << a << endl;
    cout << "getVal_v2: " << b << endl;

    return 0;
}

编译运行都ok,输出如下,
这里写图片描述
有点懵,那这个const有什么作用呢?

要回答这个问题,需要先回顾一下类的this指针。

二 回顾this指针

继续分析上一节的程序,当我们实例化类TestClass生成了对象tc,然后调用成员函数getVal_v1(),就可以拿到tc的private变量val,看上去一气呵成,但是这里需要思考一个问题:getVal_v1()是怎么拿到val的呢?

这就是靠this指针来完成的,成员函数通过this这个额外的隐式参数来访问调用它的对象。当我们调用成员函数时,会用对象的地址来初始化this指针(对象就是调用成员函数的对象)。这样我们在成员函数里就可以拿到对象的私有变量了。
getVal_v1()函数也可以写成如下形式,(一般为了方便会省去this)

int getVal_v1()
{
    return this->val;
}

this形参是隐式定义的,不会显式地出现在成员函数形参列表里,但实际是存在的,所以任何自定义为this的参数或变量都是非法的。
因为this指针总是指向这个对象,所以this是一个常量指针,不允许修改this的值。this指针的默认初始化过程等价如下,
TestClass *const this = &tc;

ps:常量指针和指针常量是不同意思,常量指针是说指针的值是const的,指针常量是说指针指向的变量的值是const的,还有常指针常量,看字面就知道是把常量指针和指针常量合在一起,不但指针本身是const的,而且指针指向的变量也是const的。OMG!!!

三 成员函数后的const

下面就把目光转向本文开始讨论的问题:成员函数形参列表后的const有什么作用?

成员函数形参列表后带const,这样的成员函数叫做类的const成员函数,也叫常量成员函数。这个const作用是用来修改隐式this指针的类型,把它变成常指针常量(请阅读上一节的ps部分,如果还看不懂,就请查阅《C++ primer 5th》2.4.2节)。

为何要转变?这里以上一节的代码为例,默认情况下,this指针是常量指针,但是如果tc是个常量对象,那么this的默认初始化就是非法的,C++规定只能使用指向常量的指针来存放常量对象的地址。这里修改下第二节的代码,如下,

#include <iostream>

using namespace std;


class TestClass
{
public:
    TestClass() : val(100) {}
    virtual ~TestClass() {}


    int getVal_v1()
    {
        return val;
    }

    int getVal_v2() const
    {
        return val;
    }

private:
    int val;
};


int main()
{
    const TestClass tc;

    int a = tc.getVal_v1();
    int b = tc.getVal_v2();

    cout << "getVal_v1: " << a << endl;
    cout << "getVal_v2: " << b << endl;

    return 0;
}

这里把tc声明为常量对象,此时再编译就出错了。
这里有2个解决办法:
1. 在成员函数getVal_v1()的形参列表后加上const,编译就ok了,也可以正常运行;
2. 不加const,然后在main函数里注释掉getVal_v1()的相关语句,程序也可以正常编译运行,如下,(可以看出常量对象只能调用常量成员函数)

int main()
{
    const TestClass tc;

    // int a = tc.getVal_v1();
    int b = tc.getVal_v2();

    // cout << "getVal_v1: " << a << endl;
    cout << "getVal_v2: " << b << endl;

    return 0;
}

讲到这里,就弄明白这个const的作用了,就是把this指针由默认的常量指针变成常指针常量。有了const后,this指针的初始化过程就等价如下,
const TestClass *const this = &tc;
因为此时this是指向常量的,所以常量成员函数不能改变调用它的对象的内容,也就是不能修改对象的私有变量值。

另外,非常量对象可以调用常量成员函数,但也是无法修改对象的内容的。

四 结语

经过上述三节,就搞明白成员函数形参列表后const的作用了,简单概述下,就是把this指针由默认的常量指针变成常指针常量,然后常量对象就可以调用这些常量成员函数了。

这里再贴一个《C++ primer 5th》中关于常量成员函数的note:
常量对象,以及常量对象的引用或指针都只能调用常量成员函数。

本文主要参考《C++ primer 5th》关于常量成员函数的讲述,也算是总结一下自己的理解。

如果有写的不对的地方,希望能留言指正,谢谢阅读。

猜你喜欢

转载自blog.csdn.net/whahu1989/article/details/80501685
今日推荐