C++箴言:只要可能就用const(2)
[position] // call const version of op[]
);
}
...
};
正如你看到的,代码中有两处强制转型,而不止一处。我们让 non-const operator[] 调用 const 版本,但是,如果在 non-const operator[] 的内部,我们仅仅调用了 operator[],那我们将递归调用我们自己一百万次甚至更多。为了避免无限递归,我们必须明确指出我们要调用 const operator[],但是没有直接的办法能做到这一点,于是我们将 *this 从它本来的类型 TextBlock& 强制转型到 const TextBlock&。是的,我们使用强制转型为它加上了 const!所以我们有两次强制转型:第一次是为 *this 加上 const(目的是当我们调用 operator[] 时调用的是 const 版本),第二次是从 const operator[] 的返回值之中去掉 const。
增加 const 的强制转型是一次安全的转换(从一个 non-const 对象到一个 const 对象),所以我们用 static_cast 来做。去掉 const 的强制转型可以用 const_cast 来完成,在这里我们没有别的选择。
在完成其它事情的基础上,我们在此例中调用了一个操作符,所以,语法看上去有些奇怪。导致其不会赢得选美比赛,但是它通过在 const 版本的 operator[] 之上实现其 non-const 版本而避免重复代码的方法达到了预期的效果。使用丑陋的语法达到目标是否值得最好由你自己决定,但是这种在一个 const 成员函数的基础上实现它的 non-const 版本的技术却非常值得掌握。
更加值得知道的是做这件事的反向方法——通过用 const 版本调用 non-const 版本来避免代码重复——是你不能做的。记住,一个 const 成员函数承诺不会改变它的对象的逻辑状态,但是一个 non-const 成员函数不会做这样的承诺。如果你从一个 const 成员函数调用一个 non-const 成员函数,你将面临你承诺不会变化的对象被改变的风险。这就是为什么使用一个 const 成员函数调用一个 non-const 成员函数是错误的,对象可能会被改变。实际上,那样的代码如果想通过编译,你必须用一个 const_cast 来去掉 *this 的 const,这样做是一个显而易见的麻烦。而反向的调用——就像我在上面的例子中用的——是安全的:一个 non-const 成员函数对一个对象能够为所欲为,所以调用一个 const 成员函数也没有任何风险。这就是 static_cast 可以在这里工作的原因:这里没有 const-related 危险。
就像在本文开始我所说的,const 是一件美妙的东西。在指针和迭代器上,在涉及对象的指针,迭代器和引用上,在函数参数和返回值上,在局部变量上,在成员函数上,const 是一个强有力的盟友。只要可能就用它,你会为你所做的感到高兴。
Things to Remember
·将某些东西声明为 const 有助于编译器发现使用错误。const 能被用于对象的任何范围,用于函数参数和返回类型,用于整个成员函数。
·编译器坚持二进制位常量性,但是你应该用概念上的常量性(conceptual constness)来编程。(此处原文有误,conceptual constness 为作者在本书第二版中对 logical constness 的称呼,正文中的称呼改了,此处却没有改。其实此处还是作者新加的部分,却使用了旧的术语,怪!——译者)
·当 const 和 non-const 成员函数具有本质上相同的实现的时候,使用 non-const 版本调用 const 版本可以避免重复代码。
- 最新评论
