这个 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.