8.8 关键字:mutable¶
功能¶
C++编译器强制实施bitwise constness,mutable可以释放掉const成员函数中non-static变量的bitwise constness约束。
bitwise constness与logical constness¶
关于const成员函数有两类说法:
bitwise constness(或称physical constness):成员函数只有在不更改对象任何成员变量(static除外)时才可以说是const的,即不更改对象内的任何一个bit。
logical constness:一个const成员函数可以修改它所处理的对象内的某些bits,但只有在客户端侦测不出的情况下才得如此。
1. bitwise constness存在的问题¶
bitwise constness正是C++对常量性(constness)的定义,因此const成员函数不可以更改对象内任何non-static成员变量。
尽管编译器强制实施bitwise constness,但你编写程序的时候应该尽量使用“概念上的常量”。
不幸的是许多成员函数虽然不十足具备const性质但却能通过bitwise测试。更具体地说,一个更改了“指针所指物”的成员函数虽然不能算是const,但如果只有指针(而非其所指物)隶属于对象,那么称此函数为bitwise const不会引发编译器异议。
举个例子,CTextBlock类返回一个reference指向对象内部值,但是却不适当地将其operator[]声明为const成员函数。不过operator[]实现代码并不更改text_ptr_,因此编译器认为它是bitwise constness并产出目标码。
class CTextBlock {
public:
explicit CTextBlock(const char *text) {
text_ptr_ = new char[std::strlen(text) + 1];
snprintf(text_ptr_, std::strlen(text) + 1, text);
}
~CTextBlock() {
delete text_ptr_;
text_ptr_ = nullptr;
}
void print() const {
std::cout << text_ptr_ << std::endl;
}
char& operator[](std::size_t position) const {
return text_ptr_[position];
}
private:
char* text_ptr_;
};
int main() {
const CTextBlock foo("Cat"); // 声明一个常量
foo.print();
foo[0] = 'D';
foo[1] = 'o';
foo[2] = 'g';
foo.print();
return 0;
}
// 输出:
$g++ -g test.cpp -o test -std=c++11
$./test
Cat
Dog
root
在上面的例子中,我们创建了一个常量CTextBlock且调用了它的const成员函数operator[],最终还是修改了它的值。
2. logic constness¶
bitwise constness这种情况导出所谓的logic constness,这一派拥护者主张一个const成员函数可以修改它所处理的对象内的某些bits,但只有在客户端侦测不出的情况下才得如此。例如你的CTextBlock类有可能高速缓存(cache)文本区块的长度以便应付询问。
class CTextBlock {
public:
std::size_t length() const {
if (!length_is_valid_) {
text_length_ = std::strlen(text_ptr_); // 编译不通过, 在const成员函数
length_is_valid_ = true; // 内不能给成员变量赋值
}
}
private:
char* text_ptr_;
std::size_t text_length_; // 最近一次计算的文本区块长度
bool length_is_valid_; // 目前的长度是否有效
};
成员函数length()并不是bitwise const的,因为text_length_和length_is_valid_变量都可能被修改。这两个成员变量被修改对const CTextBlock变量而言是可以接受的,但是编译器并不接受(编译器只同意bitwise constness),这该怎么办?
3. 基于mutable实现logical constness¶
解决方法很简单:利用C++一个与const相关的关键字mutable(可变的),它可以释放掉non-static变量的bitwise constness约束。
class CTextBlock {
public:
std::size_t length() const {
if (!length_is_valid_) {
text_length_ = std::strlen(text_ptr_); // 这些成员变量可能被修改, 即使
length_is_valid_ = true; // 在const成员函数内部
}
}
private:
char* text_ptr_;
mutable std::size_t text_length_; // 最近一次计算的文本区块长度
mutable bool length_is_valid_; // 目前的长度是否有效
};