作为文档的 Const 正确性
关于 const
正确性的一个更有用的方面是它可以作为记录代码的一种方式,为程序员和其他用户提供某些保证。这些保证由编译器强制执行,因为 const
ness 缺乏 const
ness,表明代码不提供它们。
const
CV-Qualified Member Functions:
- 可以假设任何成员函数
const
都有意读取实例,并且:- 不得修改调用它们的实例的逻辑状态。因此,除了
mutable
变量之外,它们不应修改它们被调用的实例的任何成员变量。 - 除
mutable
变量外,不应调用任何其他可修改实例成员变量的函数。
- 不得修改调用它们的实例的逻辑状态。因此,除了
- 相反,可以假定任何非
const
的成员函数都有意修改实例,并且:- 可能会也可能不会修改逻辑状态。
- 可能会或可能不会调用其他修改逻辑状态的函数。
这可用于在调用任何给定的成员函数之后对对象的状态进行假设,即使没有看到该函数的定义:
// ConstMemberFunctions.h
class ConstMemberFunctions {
int val;
mutable int cache;
mutable bool state_changed;
public:
// Constructor clearly changes logical state. No assumptions necessary.
ConstMemberFunctions(int v = 0);
// We can assume this function doesn't change logical state, and doesn't call
// set_val(). It may or may not call squared_calc() or bad_func().
int calc() const;
// We can assume this function doesn't change logical state, and doesn't call
// set_val(). It may or may not call calc() or bad_func().
int squared_calc() const;
// We can assume this function doesn't change logical state, and doesn't call
// set_val(). It may or may not call calc() or squared_calc().
void bad_func() const;
// We can assume this function changes logical state, and may or may not call
// calc(), squared_calc(), or bad_func().
void set_val(int v);
};
由于 const
规则,这些假设实际上将由编译器强制执行。
// ConstMemberFunctions.cpp
ConstMemberFunctions::ConstMemberFunctions(int v /* = 0*/)
: cache(0), val(v), state_changed(true) {}
// Our assumption was correct.
int ConstMemberFunctions::calc() const {
if (state_changed) {
cache = 3 * val;
state_changed = false;
}
return cache;
}
// Our assumption was correct.
int ConstMemberFunctions::squared_calc() const {
return calc() * calc();
}
// Our assumption was incorrect.
// Function fails to compile, due to `this` losing qualifiers.
void ConstMemberFunctions::bad_func() const {
set_val(863);
}
// Our assumption was correct.
void ConstMemberFunctions::set_val(int v) {
if (v != val) {
val = v;
state_changed = true;
}
}
const
功能参数:
- 可以假设具有一个或多个参数
const
的任何函数都有意读取这些参数,并且:- 不得修改这些参数,也不要调用任何会修改它们的成员函数。
- 不要将这些参数传递给任何其他修改它们的函数和/或调用任何会修改它们的成员函数。
- 相反,可以假设任何具有一个或多个不是
const
的参数的函数都有意修改这些参数,并且:- 可能会或可能不会修改这些参数,或调用任何可以修改它们的成员函数。
- 可能会或可能不会将这些参数传递给其他函数,这些函数会修改它们和/或调用任何会修改它们的成员函数。
这可用于在传递给任何给定函数之后对参数状态进行假设,即使没有看到该函数的定义。
// function_parameter.h
// We can assume that c isn't modified (and c.set_val() isn't called), and isn't passed
// to non_qualified_function_parameter(). If passed to one_const_one_not(), it is the first
// parameter.
void const_function_parameter(const ConstMemberFunctions& c);
// We can assume that c is modified and/or c.set_val() is called, and may or may not be passed
// to any of these functions. If passed to one_const_one_not, it may be either parameter.
void non_qualified_function_parameter(ConstMemberFunctions& c);
// We can assume that:
// l is not modified, and l.set_val() won't be called.
// l may or may not be passed to const_function_parameter().
// r is modified, and/or r.set_val() may be called.
// r may or may not be passed to either of the preceding functions.
void one_const_one_not(const ConstMemberFunctions& l, ConstMemberFunctions& r);
// We can assume that c isn't modified (and c.set_val() isn't called), and isn't passed
// to non_qualified_function_parameter(). If passed to one_const_one_not(), it is the first
// parameter.
void bad_parameter(const ConstMemberFunctions& c);
由于 const
规则,这些假设实际上将由编译器强制执行。
// function_parameter.cpp
// Our assumption was correct.
void const_function_parameter(const ConstMemberFunctions& c) {
std::cout << "With the current value, the output is: " << c.calc() << '\n'
<< "If squared, it's: " << c.squared_calc()
<< std::endl;
}
// Our assumption was correct.
void non_qualified_function_parameter(ConstMemberFunctions& c) {
c.set_val(42);
std::cout << "For the value 42, the output is: " << c.calc() << '\n'
<< "If squared, it's: " << c.squared_calc()
<< std::endl;
}
// Our assumption was correct, in the ugliest possible way.
// Note that const correctness doesn't prevent encapsulation from intentionally being broken,
// it merely prevents code from having write access when it doesn't need it.
void one_const_one_not(const ConstMemberFunctions& l, ConstMemberFunctions& r) {
// Let's just punch access modifiers and common sense in the face here.
struct Machiavelli {
int val;
int unimportant;
bool state_changed;
};
reinterpret_cast<Machiavelli&>(r).val = l.calc();
reinterpret_cast<Machiavelli&>(r).state_changed = true;
const_function_parameter(l);
const_function_parameter(r);
}
// Our assumption was incorrect.
// Function fails to compile, due to `this` losing qualifiers in c.set_val().
void bad_parameter(const ConstMemberFunctions& c) {
c.set_val(18);
}
虽然它是可以规避 const
正确性 ,并通过扩展打破这些保证,这必须是故意程序员(就像打破封装与 Machiavelli
,以上)完成,并有可能导致未定义的行为。
class DealBreaker : public ConstMemberFunctions {
public:
DealBreaker(int v = 0);
// A foreboding name, but it's const...
void no_guarantees() const;
}
DealBreaker::DealBreaker(int v /* = 0 */) : ConstMemberFunctions(v) {}
// Our assumption was incorrect.
// const_cast removes const-ness, making the compiler think we know what we're doing.
void DealBreaker::no_guarantees() const {
const_cast<DealBreaker*>(this)->set_val(823);
}
// ...
const DealBreaker d(50);
d.no_guarantees(); // Undefined behaviour: d really IS const, it may or may not be modified.
但是,由于这需要程序员非常明确地告诉编译器他们打算忽略 const
ness,并且在编译器之间不一致,所以通常可以安全地假设 const
正确的代码将不会这样做,除非另有说明。