常成员函数,它的形式是在成员函数后面加上const修饰,它是为了防止特定函数改变本类对象的数据成员而产生的,目的就是保护数据成员。在常成员函数中,你不能改变数据成员的值,你也不能在常成员函数中调用非常量成员函数,因为你不确定你调用的那个非常量成员函数是否改变了数据成员的值,如果改变了,那就相当于在常成员函数里改变了数据成员,与本身的const修饰相违背。(这里说明一下,没有常全局函数之说,因为常函数的设计是为了保护数据成员的,所以只能用在可以封装成对象的结构里面。)
对常成员函数的作用可谓是一目了然,很好理解,但是对返回值为常量的成员函数就不是那么好理解了。
先谈谈返回值为常量的函数的形式吧,返回值为常量的函数分为返回值为常量的成员函数和返回值为常量的全局函数,两者形式相同,用法也相同,其形式就是在函数前加const修饰。有人可能会问,返回值为常量的函数是为了什么设计的,不是所有返回值都相当于是常量的吗?
对一般的函数确实如此,但是对于重载的操作符就不一样了。请看如下代码:
这里我重载的+操作符返回值没有用const修饰,也就是返回值不为常量,那么下面的代码就成立了:class constSample { public: constSample() { m_data=-1; } constSample(int data) { m_data=data; } ~constSample() { } int getData() const { return m_data; } constSample operator + (const constSample & lhs) { m_data+=lhs.m_data; return *this; } private: int m_data; };
int main() { constSample sample1(3); constSample sample2(3); constSample sample3(5); cout<<"运算前:"<<endl; cout<<sample1.getData()<<" "<<sample2.getData() <<" "<<sample3.getData()<<endl; sample1+sample2=sample3; cout<<"运算后:"<<endl; cout<<sample1.getData()<<" "<<sample2.getData() <<" "<<sample3.getData()<<endl; return 0; }
下面是运行结果:
运算前:
3 3 5
运算后:
6 3 5
最后sample1的数据成员的值是6,而不是我们期待的5,这是因为+的优先级比=要高,所以先计算sample1+sample2的值,结果是保存到sample1里面,为5,然后进行赋值操作,将sample3的值赋给sample1,结果就是sample3的值。这里,我们看出来了+的结果被覆盖了,所以这么做毫无意义。
试想,如果你在C++中用如下代码编译能通过吗:int main() { int a=1,b=2,c=3; a+b=c; cout<<a<<" "<<b<<" "<<c<<endl;
return 0; }
结果是显然的,赋值号左边的表达式必须是可修改的左值,而我们上面自己定义的类能编译通过,而且还能得到结果,就是因为我们的返回值没有加const修饰,当加上const修饰之后,+操作符返回的*this就相当于一个常量,自然不能放在赋值号左边,编译器就会提示错误了。
总结一下,常成员函数的设计是为了保护数据成员,而返回值为常量的函数的设计是为了防止返回值作为左值时出现的不可预料的结果。这里说明一下,一般用const修饰返回值为对象本身(非引用和指针)的情况多用于二目操作符重载函数并产生新对象的时候。