条款03:尽可能使用const
这个条款的内容叙述了很多,不便全部列出。但主要还是围绕在对const的优点的阐述上,和如何正确与合理使用const。
const的一件奇妙的事情是,它允许你指定一个语义约束,而编译器会强制实施这项约束。它允许你告诉编译器和其他程序员某个数值应该保持不变。只要这(某值保持不变)是事实,你就确实该说出来,因为说出来可以获得编译器的相助,确保这条约束不被违反。
既然使用const修饰了某个对象,就应该保证不试图去修改它,也不应该不将它传给某个函数的non-const版本,否则non-const函数在试图修改const变量时,编译器会报错(来自编译器友好的帮助)。
注意:虽然不能直接修改const修饰的对象,但是C/C++强大的指针功能却往往可以通过强制类型转换其地址,将一个普通的指针指向它,但是要注意:这种不在C++标准中有明确的定义的行为,是一种Undefined Behaviour(UB)行为。编译器可以自己对其进行判断和处理(或许会修改原值,也或许不会修改),是一种不确定性很大的行为。
在一些const修饰的函数内,我们可能确实想要修改对象一些值,虽然对这些值来说,修改他们是可以被接受的;但是对编译器来说是绝对不同意的。这种情况的解决办法很简单:利用C++的一个与const相关的摆动场:mutable(可变的),mutable释放掉non-const成员变量的bitwise constness 约束。
对于一个函数,我们可能定义了它的const版本和non-const版本,这种情况下,如果不想造成冗余、安全性检测、额外的编译时间等问题时,可以使用下面种方法进行代码复用(虽然转型是一个糟糕的想法):
例:
class TextBlock{ //原始版本
public:
...
const char & operator[](std::size_t position) const
{
...//边界检测
...//数据完整性
return text[position] ;
}
char & operator[](std::size_t position)
{
...//边界检测
...//数据完整性
return text[position] ;
}
private:
std::string text;
};
class TextBlock{ //non-const调用const的版本
public:
...
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) )
[positon]
);
}
private:
std::string text;
};
这份代码有两个转型动作:
- 先用static_cast<>给(*this)添加上const属性,并调用operator [],这样调用的就是const版本的operator[],而不是non-const 版本的operator[]。
- 在用const_cast<>移除operator[] 返回值的const。
请记住:
- 将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体;
- 编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”(conceptual constness);
- 当cosnt和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复