Class A
{
public:
void f() {cout << i <<endl;}
private:
int i = 0;
//...
};
A a;
a.f();
类的成员函数有一个隐式的指针参数——this,this总是绑定到调用成员函数的对象,比如上面的a,即编译器会令this指针拥有a的地址。上面函数f要输出i的值,这个i是类对象a的数据成员i,这是通过this指针来实现的,相当于:
//伪代码,类成员函数相当于:
A::f(A* const this) //隐式参数写出来了
{ cout << this->i << endl;}
//a.f()这个调用相当于把a的地址赋给this:
A::f(&a);
//于是函数体中的语句执行变成:
cout << (&a)->i << endl;//输出的自然是a的i成员了
从上面可以看出this的默认类型是top-level const,即指向non-const类类型的const指针——A* const this;,于是我们不能令它指向一个const A类型对象,比如:
A* const this; //(用于演示)
const A ca;
this = &ca; //(用于演示)错误!ca是常量,不能绑定到指向非常量的指针
ca.f(); //所以这个调用就是错误的
为什么常量不能绑定到指向非常量的指针?因为如果可以这样做,我们就能通过指针改变常量的值!那常量的const属性也就无从谈起了:
//如果以上绑定合法
modify(*this);//ok,ca能算常量吗?
那类的常量对象比如ca它能调用什么样的成员函数呢?答案是只能调用const成员函数:
Class A
{
public:
void f() const //常成员函数
{cout << i <<endl;}
private:
int i = 0;
//...
};
const A ca;
ca.f();//这次调用合法了
因为常成员函数 void f() const;中的const的作用正是:修改隐式指针this使它具备low-level const属性,即this现在变为指向const的const指针——const A* const this;
而这个版本的this指针既可以指向常量类对象,又可以指向非常量类对象:
//用于演示:
const A* const this;
this = &a; //ok
//or
this = &ca; //ok
为什么一个非常量类对象能绑定到一个指向常量的指针呢?因为这种绑定是安全的,我们不能通过这个指针对对象进行任何写操作,从对象的角度看这个指针,是限制了对象本身的可变性,这种转换是令系统更稳定的一种转换,所以会被允许。
于是这样的调用:
//都合法
a.f();
ca.f();
总结:在对对象进行只读操作时,我们看到常成员函数具有更广泛的适用性,它既能被常量对象调用,又能被非常量对象调用,而且常量对象只能调用常成员函数,但是要对对象进行写操作,就必须使用非常的成员函数了。