2. char* pc = &cctb[0];//调用const operator[]取得一个指针, 指向cctb的数据。
3. *pc = 'J'; //cctb现在有了 "Jello" 这样的内容。
以上代码没有任何错误:创建一个常量对象并设以某值,而且只对它调用const成员函数。但终究还是改变了它的值。
这种情况导出所谓的logical constness:一个const成员函数可以修改它所处理的对象内的某些bits,但只有在客户端侦测不出的情况下才得如此,也就是自己可以改用户不能改。
1. class CTextBlock {
2. public:
3. ...
4. std::size_t length() const;
5. private:
6. char* pText;
7. std::size_t textLength; //最近一次计算的文本区块长度。
8. bool lengthIsValid; //目前的长度是否有效。
9. };
10. std::size_t CTextBlock::length() const
11. {
12. if (!lengthIsValid) {
13. textLength = std::strlen(pText);
14. //错误!在const成员函数内不能对私有变量textLength进行修改
15. lengthIsValid = true;
16. //错误!在const成员函数内不能对私有变量lengthIsValid进行修改
17. }
18. return textLength; }
解决办法很简单:利用C++ 中的mutable(可变的)修饰符,mutable释放掉non-static成员变量的bitwiseconstness约束:
1. class CTextBlock {
2. public:
3. ...
4. std::size_t length() const;
5. private:
6. char* pText;
7. mutable std::size_t textLength; //这些成员变量可能总是会被更改,
8. mutable bool lengthIsValid; //即使在const成员函数内。
9. }; //现在,刚才的length函数就可以了~
l 编译器强制实施bitwise constness,但你编写程序时应该使用"概念上的常量性"(conceptual constness)。
在const和non-const成员函数中避免重复
对于"bitwise-constness非我所欲"的问题,mutable是个解决办法,但它不能解决所有的const相关难题。举个例子,假设TextBlock(和 CTextBlock)内的operator[] 不单只是返回一个reference指向某字符,也执行边界检验(boundschecking)、志记访问信息(loggedaccess info.)、甚至可能进行数据完善性检验。把所有这些同时放进const和non-const operator[] 中,导致两个版本的operator[]及大量的代码重复。
真正该做的是实现operator[]的机能一次并使用它两次,也就是说,令其中一个调用另一个。本例中constoperator[]完全做掉了non-const版本该做的一切,唯一的不同是其返回类型多了一个const资格修饰。
1. class TextBlock {
2. public:
3. ...
4. const char& operator[](std::size_t position) const //一如既往
5. {
6. ...
7. return text[position];
8. }
9. char& operator[](std::size_t position) //现在只调用const op[]
10. {
11. return
12. const_cast
13. static_cast
14. [position] //调用const op[]
15. );
16. }
17. ...
18. };
这里共有两次转型:第一次用来为 *this添加const(这使接下来调用operator[]时得以调用const版本),第二次则是从constoperator[]的返回值中移除const。
添加const的那一次转型强迫进行了一次安全转型(将non-const对象转为const对象),所以我们使用static_cast。移除const的那个动作只可以藉由const_cast完成,没有其他选择。
简单来说就是non-const版本为了调用const版本先转换常量性,应用const版本功能完毕后,为了符合non-const的返回值,再去除常量性。
const成员函数承诺绝不改变其对象的逻辑状态(logicalstate),non-const成员函数却没有这般承诺。如果在const函数内调用non-const函数,就是冒了这样的风险:你曾经承诺不改动的那个对象被改动了。这就是为什么"const成员函数调用non-const成员函数"是一种错误行为:因为对象有可能因此被改动。
l 当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。
摘自 pandawuwyj的专栏