這個 Pointer CV-Qualifiers
this
也可以是 cv 限定的,與任何其他指標相同。但是,由於 this
引數未在引數列表中列出,因此需要特殊語法; cv 限定符列在引數列表之後,但在函式體之前。
struct ThisCVQ {
void no_qualifier() {} // "this" is: ThisCVQ*
void c_qualifier() const {} // "this" is: const ThisCVQ*
void v_qualifier() volatile {} // "this" is: volatile ThisCVQ*
void cv_qualifier() const volatile {} // "this" is: const volatile ThisCVQ*
};
由於 this
是一個引數,因此可以根據其 this
cv-qualifier 過載函式 。
struct CVOverload {
int func() { return 3; }
int func() const { return 33; }
int func() volatile { return 333; }
int func() const volatile { return 3333; }
};
當 this
是 const
(包括 const volatile
)時,該函式無法通過它寫入成員變數,無論是隱式還是顯式。唯一的例外是 mutable
成員變數 ,無論常量如何都可以寫入。因此,const
用於指示成員函式不會更改物件的邏輯狀態(物件對外界的顯示方式),即使它確實修改了物理狀態(物件在引擎蓋下看起來的方式) )。
邏輯狀態是物件對外部觀察者的顯示方式。它與物理狀態沒有直接聯絡,實際上甚至可能不會被儲存為物理狀態。只要外部觀察者看不到任何變化,即使你翻轉物件中的每一個位,邏輯狀態也是恆定的。
物理狀態,也稱為按位狀態,是物件儲存在記憶體中的方式。這是物件的細節,構成其資料的原始 1 和 0。如果物件在記憶體中的表示永遠不會改變,那麼它只是物理上不變
請注意,C++基於邏輯狀態而不是物理狀態來定義 const
ness。
class DoSomethingComplexAndOrExpensive {
mutable ResultType cached_result;
mutable bool state_changed;
ResultType calculate_result();
void modify_somehow(const Param& p);
// ...
public:
DoSomethingComplexAndOrExpensive(Param p) : state_changed(true) {
modify_somehow(p);
}
void change_state(Param p) {
modify_somehow(p);
state_changed = true;
}
// Return some complex and/or expensive-to-calculate result.
// As this has no reason to modify logical state, it is marked as "const".
ResultType get_result() const;
};
ResultType DoSomethingComplexAndOrExpensive::get_result() const {
// cached_result and state_changed can be modified, even with a const "this" pointer.
// Even though the function doesn't modify logical state, it does modify physical state
// by caching the result, so it doesn't need to be recalculated every time the function
// is called. This is indicated by cached_result and state_changed being mutable.
if (state_changed) {
cached_result = calculate_result();
state_changed = false;
}
return cached_result;
}
請注意,雖然你在技術上可以在 this
上使用 const_cast
來使它成為非 cv 合格的,但你真的,真的不應該,而且應該使用 mutable
代替。當 const_cast
用於實際上是 const
的物件時,它可能會呼叫未定義的行為,而 mutable
被設計為可以安全使用。但是,你可能會在極其舊的程式碼中遇到此問題。
此規則的一個例外是根據 const
訪問器定義非 cv 限定的訪問器; 因為如果呼叫非 cv 限定版本,物件被保證不是 const
,那麼就沒有 UB 的風險。
class CVAccessor {
int arr[5];
public:
const int& get_arr_element(size_t i) const { return arr[i]; }
int& get_arr_element(size_t i) {
return const_cast<int&>(const_cast<const CVAccessor*>(this)->get_arr_element(i));
}
};
這可以防止不必要的程式碼重複。
與常規指標一樣,如果 this
是 volatile
(包括 const volatile
),則每次訪問時都會從記憶體載入,而不是快取。這對於優化具有與宣告任何其他指標 volatile
相同的效果,因此應該小心。
請注意,如果例項是 cv 限定的,則允許訪問的唯一成員函式是成員函式,其 this
指標至少與例項本身一樣是 cv 限定的:
- 非 cv 例項可以訪問任何成員函式。
const
例項可以訪問const
和const volatile
函式。volatile
例項可以訪問volatile
和const volatile
函式。const volatile
例項可以訪問const volatile
函式。
這是 const
正確性的關鍵原則之一。
struct CVAccess {
void func() {}
void func_c() const {}
void func_v() volatile {}
void func_cv() const volatile {}
};
CVAccess cva;
cva.func(); // Good.
cva.func_c(); // Good.
cva.func_v(); // Good.
cva.func_cv(); // Good.
const CVAccess c_cva;
c_cva.func(); // Error.
c_cva.func_c(); // Good.
c_cva.func_v(); // Error.
c_cva.func_cv(); // Good.
volatile CVAccess v_cva;
v_cva.func(); // Error.
v_cva.func_c(); // Error.
v_cva.func_v(); // Good.
v_cva.func_cv(); // Good.
const volatile CVAccess cv_cva;
cv_cva.func(); // Error.
cv_cva.func_c(); // Error.
cv_cva.func_v(); // Error.
cv_cva.func_cv(); // Good.