1.什么是常成员函数
常成员函数,它就是在成员函数后面加一个const,用来修饰this指针所指的对象,使this指针所指的对象不受更改,保护它的数据安全。当常成员函数没有参数的时候修饰的是this指针,有参数的时候参数不受影响,还是修饰的this!!常成员函数内部可以引用const数据成员,也可以引用非const数据成员。总之,常成员函数只是限定了使用此常成员函数的引用对象(this)不发生改变而已,没别的意思。其实这样做的目的是为了与常对象相匹配,因为常对象只能调用它的常成员函数。可以参考谭浩强c++p90。
int getId() const
{
return this->id;
}
比如上面的成员函数就是一个常成员函数,const是修饰this指针所指的对象的,我们知道this指针其实就是一个指向对象的常指针,另外说一句c++中的引用也是传递的常指针,那么这个常成员函数可以表示成这样(假设它是我定义的Student类里面的常成员函数):
int getId(const Student * const this)
{
return this->id;
}
其实常成员函数的内部实现就是上面这样的,只是我们不这样书写,后面的const修饰的是this指针(即这个this指针不能再加减,成为一个常量),前面的const修饰的是这个this常指针所指的对象(即这个对象不受改变)。
2.常对象和常成员函数应该注意的事情
我们先看一个demo
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//创建一个Student类,包含id和name
class Student
{
private:
int id;
char *name;
public:
Student(){
this->id = 0;
this->name = NULL;
}
Student(int id, char *name) {
this->id = 0;
int len = strlen(name);
this->name = new char[len + 1];
strcpy(this->name, name);
}
int getId() const
{
return this->id;
}
~Student() {
if (name != NULL) {
delete name;
name = NULL;//当光标在这个位置的时候按 Shift+},光标就会跳到下面的大括号外
}
}
};
//全局函数display是为了接受一个Student对象的引用,然后为了保证display函数不会修改这个引用对象,就在前加了一个const修饰
//但是在用std.getId()输出的时候爆出错误:对象含有与成员函数Student::getId不兼容的类型限定符,对象类型是const Student
//解决的办法就是在对象的成员函数int getId(){}后面加一个const,修饰this指针所指的对象,让这个所指的对象不发生改变。
//关键有一句话:常对象只能调用它的常成员函数,下面display函数中接收的是常对象的std,所以只能调用常成员函数,而Student类中的成员函数又不是常成员函数,所以就在函数后加个const变成常成员函数。
void display(const Student & std) {
cout << std.getId() << endl;
}
int main() {
//创建一个Student对象
Student s1(1, "zhang3");
//调用全局函数display
display(s1);
}
根据以上demo我们知道,如果在display()的形参中加了一个const修饰的话,那么里面调用的getId()函数也需要在Student类里面声明为const 常成员函数。当然如果形参中不加const修饰的话就不存在这个问题了。那这个问题怎么来解释了,可以从两个角度来解释,如下:
解释一:我们看display函数中Const Student &std,表示这个传进来的Student对象是不能修改的,但是在调用std.getId()的时候,getId()会把std对象通过this指针传到这个函数里面去(只是不需要把this显示出来而已,但是实际上就这样操作了),相当于这个getId()是下面这样的:
int getId(Student * const this)
{
return this->id;
}
那么也就是说,这个student对象从一个Const Student 赋值给了 Student ,这触发了c++的安全检测机制,就是“非常的”可以赋值给“常的”,但是“常的”不能赋值给“非常的”,因为这个破坏了“常的”安全,那岂不是就能改变了吗,所以c++要求不能这样做。
所以,当我们在getId()后加上cost之后,就变成了下面这个样子了:
int getId(const Student * const this)
{
return this->id;
}
即:this指针所对应的对象不会改变还是常的!所以Const student还是赋值给了Const student。但是this指针也是不可见的,所以,最终的做法就是在getId()后面加个const!
解释二:也可以从另外一个角度来解释,我们看display()的形参中,不是给Student &std加了一个const修饰吗,就变成了
const Student & std,这是什么?这是常对象,也就是将一个普通的对象接收后变成了一个常对象,常对象只能调用它的常成员函数而不能调用该对象的普通成员函数。所以,如果想要std.getId(),那么这个函数getId()必须是常成员函数,还有一个需要注意的点,常成员函数不能改变数据成员,其实也不用死记,你都定义成常的了,你还去改变它干嘛。