C++常对象和常成员函数详解

如果希望某个对象的值初始化以后就再也不被改变,则定义该对象时可以在前面加 const 关键字,使之成为常量对象(简称“常对象”)。例如:
class CDemo{
public:
    void SetValue(){ }
};
const CDemo Obj;  // Obj 是常量对象
在 Obj 被定义为常量对象的情况下,下面这条语句是错误的,编译不能通过:
Obj.SetValue();
错误的原因是,常量对象一旦初始化后,其值就再也不能更改。因此,不能通过常量对象调用普通成员函数,因为普通成员函数在执行过程中有可能修改对象的值。

但是可以通过常量对象调用常量成员函数。所谓常量成员函数,就是在定义时加了 const 关键字的成员函数(声明时也要加)。例如:
#include<iostream>
using namespace std;
class Sample{
   public:
   void GetValue() const;  //常成员函数
};
void Sample::GetValue() const  //常成员函数
{
}
int main(){
    const Sample o;
    o.GetValue();  //常量对象上可以执行常量成员函数
    return 0;
}
上面的程序在 Visual Studio 中没有问题,但在 Dev C++ 中,要为 Sample 类编写无参构造函数才可以。在 Dev C++ 中,常量对象如果使用无参构造函数初始化,就需要显式写出无参构造函数。

常量对象上可以执行常量成员函数,是因为常量成员函数确保不会修改任何非静态成员变量的值。编译器如果发现常量成员函数内出现了有可能修改非静态成员变量的语句,就会报错。因此,常量成员函数内部也不允许调用同类的其他非常量成员函数(静态成员函数除外)。

思考题:为什么上面一段话要强调“非静态成员变量”和“静态成员函数除外”?

两个成员函数的名字和参数表相同,但一个是 const 的,一个不是,则它们算重载。例如:
#include <iostream>
using namespace std;
class CTest{
private:
    int n;
public:
    CTest(){n = 1;}
    int GetValue() const { return n; }
    int GetValue() { return 2*n; }
};
int main(){
    const CTest objTestl ;
    CTest objTest2;
    cout << objTestl.GetValue () << "," << objTest2.GetValue();
    return 0;
}
程序的输出结果是:
1, 2

可以看到,通过常量对象调用 GetValue 函数,那么被调用的就是带 const 关键字的 GetValue 函数;通过普通对象调用 GetValue 函数,被调用的就是不带 const 关键字的 GetValue 函数。

基本上,如果一个成员函数中没有调用非常量成员函数,也没有修改成员变量的值,那么将其写成常量成员函数是好的习惯。