如何理解const成员函数
在深入理解const成员函数之前,先来复习一下const关键字的基础用法。
1. const修饰某个非指针类型变量
const 修饰某个非指针类型变量,表示该变量只读。
const int a = 10;
a = 20; // 错误,不可以赋值
2. const修饰指针
const 在*号前面表示指针指向的内容不可更改,指针本身可以改变
char buf[] = "hollo, world";
const char* p = buf;
p++; //指针自增,正确
*p = 'e'; //错误,指针指向的内容不可更改
const 在*号后面表示指针本身不可改变(不能被赋值),指针指向的内容可以改变,这个指针必须在初始化的时候赋值。
char buf[] = "hollo, world";
char* const p = buf;
*p = 'H'; //正确
p++; //错误,p不可更改
const 在*号前后都加表示指针本身和指针指向的内容均不能改变。
char buf[] = "hollo, world";
const char* const p = buf;
*p = 'H'; //错误,不能改变p指向的内容
p++; //错误,p不能更改
3. const修饰成员函数
好了,到这里我们可以来探讨一下const成员函数了。首先要明确const修饰成员函数的目的是什么。const修饰成员函数是为了使该成员函数可以作用于const对象身上,用const修饰成员函数的作用实际上是修改隐式this指针的类型。
默认情况下,this指针的类型是指向类类型非常量版本的常量指针。这句话有点拗口,举个例子,假如有一个对象A,那么在A对象的成员函数中this的类型是A* const this,根据前面的内容我们知道,this指针不可以更改,但是this指针指向的对象的成员变量是可以更改的。
class A {
public:
int x;
int y;
A():x(0),y(0) {}
void set(int x, int y) {
// this的类型是 A* const
this->x = x;
this->y = y;
}
int getX() {
return this->x;
}
int getY() {
return this->y;
}
}
int main()
{
A m;
m.set(1, 2); // 正确
std::cout << m.getX(); //正确
std::cout << m.getY(); //正确
return 0;
}
尽管A* const this是隐式的,但是他仍然要遵循初始化的规则,也就是说我们不能把this绑定到一个常量对象上。那么我们也不能在一个常量对象上调用普通的成员函数。
const A m;
m.set(1, 2) //错误,m是常量对象,不能绑定到this,因此不能调用非const函数
m.getX(); //错误,m是常量对象,不能绑定到this,因此不能调用非const函数
前面说过this指针的类型是A* const,因此不能绑定到一个常量对象上。如果把this指针的类型改为const A* const,就可以把this绑定到一个常量对象上了。然而,this是隐式的并不会出现在成员函数的参数列表中,所以在哪里将this声明为指向常量的指针(const A* const)就成了一个问题。C++的做法是允许把const关键字放在成员函数的参数列表之后,此时,紧跟在参数列表后面的const表示this是一个指向常量的指针(const A* const)。像这样使用const的成员函数被称为常量成员函数。为什么要把const关键字放在参数列表后面,而不是放在前面呢?因为放在参数列表前面就成了修饰成员函数的返回值,显然不符合我们需要的功能,同时this又不能出现在参数列表中,因此只能放在参数列表后面。修改A对象的定义如下:
class A {
public:
int x;
int y;
A():x(0),y(0) {}
void set(int x, int y) {
// this的类型是 A* const
this->x = x;
this->y = y;
}
int getX() {
return this->x;
}
int getY() {
return this->y;
}
int getX() const {
return this->x;
}
int getY() const {
return this->y;
}
}
这样我们就可以愉快的调用常量的成员函数。在这里int getX()
函数和int getx() const
函数构成重载,getY两个函数同理。
const A m;
A n;
m.set(1, 2); //错误,常量对象不能调用非常量成员函数
m.getX(); //正确,调用的是常量成员函数
n.set(1, 2); //正确
n.getX(); //正确,调用的是普通成员函数
4. 总结
const的使用方法可以总结为4句话:
- const 修饰某个非指针类型变量,表示该变量只读。
- const 修饰指针,在*号前面表示指针指向的内容不可更改,指针本身可以改变。
- const 修饰指针,在*号后面表示指针本身不可改变(不能被赋值),指针指向的内容可以改变,这个指针必须在初始化的时候赋值。
- const 修饰成员函数,表示一个常量对象可以调用该成员函数。