类函数const用于告诉编译器类函数不会更改成员变量。因此,该类型的常量对象可以安全地调用它。下面是一个简单的示例。
#include <iostream>
using namespace std;
class X {
private:
int a{1};
public:
void PrintA() const {
cout << a << "\n";
}
};
int main() {
const X x;
x.PrintA();
}
我们告诉编译器#PrintA是const,因此常量对象可以安全地调用它。但是,似乎编译器实际上很聪明,可以独立于const关键字检测函数是否为只读。如果我在上面的代码中像这样添加a=10
#include <iostream>
using namespace std;
class X {
private:
int a{1};
public:
void PrintA() const {
cout << a << "\n";
a = 10;
}
};
int main() {
const X x;
x.PrintA();
}
我知道
exp.cpp: In member function ‘void X::PrintA() const’:
exp.cpp:11:9: error: assignment of member ‘X::a’ in read-only object
a = 10;
换句话说,const关键字不能欺骗编译器以允许常量对象的变异。所以我的问题是,为什么开发人员需要声明方法const?看起来,即使没有该提示,编译器也可以区分只读方法和非只读方法,因此可以正确捕获尝试使常量对象发生突变的情况。
答案 0 :(得分:4)
这不是提示-它是方法接口的一部分。如果删除const,PrintA中的错误将消失,而main中将出现错误。出于需要public和private的相同原因,您需要const来定义所需的接口。然后,编译器将检查以确保您不违反声明的接口。
答案 1 :(得分:2)
编译器区分只读方法和非只读方法
首先考虑编译器使用今天存在的const
来完成此操作的难易程度。
PrintA
的实现是否遵守规则,编译器只需要查看该实现即可。x.PrintA();
是否对const X x;
有效,只需要声明PrintA
。现在想象一下,如果我们没有功能级别的const
PrintA
的实现是否遵守规则,编译器必须确定它是否不是只读的,然后扫描整个程序以查找是否曾经在{{1}上被调用}对象。我敢肯定,这会使大型程序的链接时间大大膨胀。
但是const
函数是一个值得关注的问题。想象一下,一个派生类使用只读实现覆盖,但是另一种派生类使用非只读实现覆盖。然后,如果在virtual
对象上调用了这种方法,那么编译器将要做什么,因为它可能无法在编译时确定将调用哪种实现?我们是否只需要排除虚函数就不可能调用const对象?不幸的是,这是有限的。
此外,当调用者与实现跨越DLL边界(即使对于非虚拟函数)分开时,这种想法也行不通,因为它们仅在运行时连接在一起。
因此,总的来说,如果我们要让编译器不得不弄清楚方法是否以const方式实现,那么要声明const对象就显得更加困难/困难。