在类的成员函数的参数列表后面加上关键字const,表示它是一个const成员函数。考虑下面的类:
1 | class Foo { |
其中,value()
和cvalue()
功能相同,但cvalue()
是const成员函数,setValue()
修改了类的成员变量。我们分别创建类的普通对象f
和常量对象cf
,并调用成员函数:
1 | Foo f; |
cf
是常量对象,无法调用修改对象内容的setValue()
,可以理解,但cf
也无法调用value()
,只能调用const成员函数cvalue()
!要解释这个问题,首先介绍一下this指针。
调用成员函数实际上是在替某个对象调用它。成员函数通过一个名为this指针的额外隐式参数来访问调用它的那个对象。value()
是普通的成员函数,int Foo::value()
的实际形式是int Foo::value(Foo *const this)
,即,this本身是一个常量(注意,不是指向常量的指针)。在调用成员函数时,编译器将调用过程f.value()
重写为int Foo::value(&f)
。
当调用常量对象cf
的成员函数时,用cf
对象的地址初始化this指针。根据初始化规则,只有指向常量的指针才能够存放常量对象的地址。正是这个原因,在常量对象上调用非常量成员函数是非法的。因此,我们应该把this声明成const Foo *const
,毕竟value()
的函数体内不会改变this所指的对象。C++的做法是将const关键字放在成员函数的参数列表之后,也就得到了const成员函数cvalue()
。
综上所述,我们可以得到const成员函数的特点:
- 使得接口容易理解,提醒调用者,该const成员函数无法修改类对象的内容
- 使得操作“常量对象”成为可能,因为常量对象只能调用const成员函数
所以,对于那些不修改对象内容的成员函数,应尽可能地定义成const,不仅能给接口的调用者传递信息,还能够扩大使用范围。