在const和non-const成员函数中避免重复
对于‘bitwise-constness‘的问题,mutable是一个解决方法,但是不能解决所有。
试想,假设TextBlock类的内部operator[]不仅仅返回一个reference指向某个字符,也执行边界检验,志记访问信息,甚至可能进行数据完整性检验。
把所有这些功能同时放入const和non-const operator[]之中,将会导致长度颇为可议的隐式inline函数。
class TextBlock {
public:
...
char &operator[](std::size_t position) {
//边界检查
//志记数据访问
//检验数据完整性
return text[position];
}
const char &operator[]() const {
//边界检查
//志记数据访问
//检验数据完整性
return text[position];
}
private:
std::string text;
};
分析:
这样写无疑会导致大量代码重复以及编译,维护,膨胀等因素等等问题。
当然,可以将边界检验… 等代码用另一个成员函数(往往是private)存放起来,并让两个operator[]调用它。但是这样做还是重复了代码。
有没有更好的方法呢?
我们真正该做的应该是实现operator[]的机能一次,并且能够使用它两次!
也就是说,想办法让其中一个调用另外一个。这促使我们将使用常量性转除。
就一般守则而言,使用转型(casting)是一个糟糕的做法。
本例中 const operator[] 完全做到了 non-const operator[] 所该做的一切,唯一的不同是,它的返回值多了一个const修饰。这种情况下,如果将返回值的const转除是安全的,因为无论谁调用 non-const operator[] 都一定首先需要有个 non-const 对象,否则就无法调用non-const函数。
所以:
令 non-const operator[] 调用其 const 兄弟是一个避免代码重复的安全做法,即使过程中需要一个转型(casting)操作。
代码如下:
//effective 03
#include <iostream>
using namespace std;
class TextBlock {
public:
TextBlock() {}
TextBlock(std::string t) : text(t) {}
~TextBlock() {}
TextBlock(const std::string &t) {
text = t;
}
TextBlock(const std::string &&t) {
std::string temp = std::move(t);
this->text = temp;
}
//const和非const版本
//const版本是必须的 基础,非const可以通过调用const实现
const char &operator[](std::size_t position) const {
return text[position];
}
char &operator[](std::size_t position) {
return const_cast<char &>(
static_cast<const TextBlock &>(*this)[position]);
}
private:
std::string text;
};
代码有两个转型操作。
我们打算让 non-const 调用其 const 兄弟,但是 non-const operator[] 若知识单纯地调用 operator[] ,就会导致递归调用自己。
所以,我们必须明确指明调用的是 const operator[] ,但是c++缺乏直接的语法。
因此,这里将 (*this)指针从它的原始类型 TextBlock & 转换为 const TextBlock &
第一次转型:为 *this 指针添加 const 修饰,使得调用 [] 时候一定是调用 const operator[]
第二次转型:从 const operator[] 的返回值中移除 const
添加 const 的第一次转换强制进行了一次安全转型(将 non-const 对象转化为 const 对象),所以我们使用 static_cast
移除 const 的那个操作只能使用 const_cast 完成。
虽然这样写,语法上第一次看有点点不懂,但是我们却能够避免代码重复,使用 const operator[] 实现了 non-const operator[] 版本。
更值得我们了解的是:
令 const 版本调用 non-const 版本以避免重复,这并不是我们该做的事情!
Rember:
const成员函数承诺绝不会改变其对象的逻辑状态,non-const成员函数却没有这样的承诺。
如果在 const 成员函数内调用了 non-const 函数,就是冒了这样的风险;曾经承诺不改变的对象可能被改变了!
这就是为什么? const 成员函数调用 non-const 成员函数是错误的行为。
所以本例使用 static_cast 作用于 *this 的原因,这里并不存在 const 相关危险。
请记住:
将某些东西声明为 const 可以帮助编译器侦测出错误用法。 const 可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。
编译器强制实施 bitwise constness ,但是你写的程序时应该使用“概念上的常量性“
当 const 和 non-const 成员函数有着实际等价的实现时,令 non-const 调用 const 版本可以避免代码重复。
补充:
bitwise constness 和 logical constness
bitwise const阵营的人相信,成员函数只有在不更改对象之任何成员变量时才能说是 const ,也就是它不更改任何一个bit。这种观点的好处是很容易侦测违反点:编译器只需要寻找成员变量的赋值动作即可。
bitwise constness 正是c++对于常量性的定义。
因此 const 成员函数不可以更改对象内任何 non-const 成员变量