为什么我们需要常量方法?

Why Do We Need const methods?

本文关键字:方法 常量 我们 为什么      更新时间:2023-10-16

class 函数 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 关键字无关。如果我像这样在上面的代码中添加一个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,PrintA 中的错误将消失,您将在 main 中收到错误。你需要 const 的原因与你需要 public 和 private 的原因相同 -- 来定义你想要的接口。然后,编译器将进行检查,以确保您没有违反已声明的接口。

编译器

区分只读和非只读方法

首先考虑编译器使用当前存在的const指定可以多么容易地执行此操作。

  • 要确定PrintA的实现是否遵守规则,编译器只需查看该实现。
  • 要确定x.PrintA();是否对const X x;有效,它只需要声明PrintA

现在想象一下,如果我们没有功能级const

  • 若要确定PrintA的实现是否遵守规则,编译器必须确定它是否不是只读的,然后扫描整个程序以查找它是否曾经在const对象上调用过。

我敢肯定,这会大大增加大型程序的链接时间。

但是,一个重要的问题是virtual功能。假设一个派生类使用只读实现重写,但随后另一个派生类使用非只读实现重写。那么,如果在const对象上调用这样的方法,编译器该怎么办,因为它可能无法在编译时确定将调用哪个实现?我们是否只需要排除虚拟可以调用常量对象?不幸的是,这将是有限的。

此外,当调用方与实现跨 DLL 边界分离时(即使对于非虚拟函数(,这个想法也行不通,因为它们仅在运行时连接在一起。

因此,总的来说,如果我们将其留给编译器来确定方法是否以常量方式实现,那么能够声明 const 对象对我们来说似乎更加困难/问题。