作為文件的 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
正確的程式碼將不會這樣做,除非另有說明。