struct SomeType
{
int m_a;
void SomeMethod()const
{
m_a = 0;
}
}; 上面的代码实际上将无法通过编译,我们把"SomeMethod"换一个写法来理解一下
void SomeMethod()const
{
this->m_a = 0;
}
这里的“this”就是我们通常说的成员函数的隐含this指针参数了,该参数虽然没在函数参数列表里(所以称之为隐含),但是这个参数是实际存在的,并且该参数类型对于非const成员函数来说是SomeType* const,对于const 成员函数,其类型是const SomeType* const, 也就是又增加了一个const(我想是由于this指针是隐含参数,所以const没地方放了,只好放在成员函数的结尾了)。这也就解释了,为什么上面的代码无法通过编译,同时也说明了,成员函数是通过这个隐含的this指针来访问其成员的,这个隐含的this指针,在代码定义的时候仍然可以省去不写,当然,在编译的时候,编译器会自动添加这个this的。
这里还存在一个很别扭的问题: 在SomeMethod的第一个版本定义里,定义的是const成员函数,但是第二个版本由于升级,我们需要改动某个成员变量的值,很显然,这个时候我们需要把SomeMethod成员函数后面的const去掉才能通过编译,但这样做又会带来一个问题,如果SomeMethod是作为共享代码库的形式存在,我们有理由保证SomeMethod的版本兼容性,这样才能完全保证该库的第一个版本使用者,在升级到该库的第二个版本时,可以不改变调用代码,进行成功编译。为了解决这个问题,C++引入mutable关键字,也就是把m_a定义为”mutable int m_a”就可以了。该关键字将屏蔽掉编译过程中对const的特殊优化处理,不仅仅解决这个编译问题,也保证了运行期的逻辑正确性。
这个时候,大家可能会提出一个”实用”的做法,就是避免定义const成员函数,一切问题不就解决了吗(函数参数的const定义规范,以后我会专门讨论)?其实用const有如下几个明显好处:
1. const从语言层面保证该方法不会改动其成员,帮助该方法的使用者理解其含义并做出正确调用,也帮助该方法的设计者不违背其实现意图,从编译层面尽可能防止写出错误的实现代码
2. const可以扩大该方法的使用范围,const SomeType c; c.SomeMethod(); 如果不是const成员函数,这个代码将无法实现编译,也就是说该方法的调用将受到本不该受到的限制
3. 从语义以外的执行层面,const变量在一定程度上会参与编译的优化,从而提高运行效率,也就是const对象的存在是必要的