摘要
const是我们的朋友:不变的值更易于理解、跟踪和分析,所以应该尽可能地使用常量代替变量,定义值的时候,应该把const作为默认的选项:常量很安全,在编译时会对其进行检查(见第14条),而且它与C++的类型系统已浑然一体。不要强制转换const的类型,除非要调用常量不正确的函数(见第94条)。
讨论
常量能够简化代码,因为只需查看定义处的代码就能知道它在各处的值了。思考以下代码:
void Fun( vector<int>& v ) {
// ……
const size_t len = v.size();
// ……更多行代码……
}
一看到上面len的定义,就能知道len在整个作用域中的语义(假设代码不会强制转换const的类型,它也不应该这样做,见下面的说明):某一特定点上v长度的瞬像。只需查看一行代码,就能知道len在整个作用域上的语义。不使用const的话,len可能会在后来被直接或通过别名修改。而且更好的是,编译器还能帮你确保这一点。
请注意const并不深[2]。例如,假设类C有一个X*类型的成员。在const的C对象中,X*成员也是const——但它所指向的X对象则不是(参阅[Saks99])。
用mutable成员实现逻辑上的不变。当类的const成员函数需要合法地修改成员变量时(即变量不影响对象的可观察状态时,比如缓存数据),声明该成员变量为mutable的。请注意,如果用Pimpl惯用法(见第43条)隐藏了所有私有成员,就无需对缓存信息或指向它的未改变的指针声明mutable了。
是的,const有“病毒性”——即使只在一处加入,当你调用其他签名不是常量正确的函数时,它也会传播到代码各处。这是一种特性,而非错误,它极大地提高了const的效力,虽然在
const还没有被人很好地理解的岁月里,这成为const广受不公正贬抑的原因。修改已有的代码,使其常量正确很花时间,但这是值得的,而且还有可能揭示出潜在的错误。
常量正确性是值得实现的,它已经得到证实而且非常有效,应该大力推荐。理解程序状态变化的方式和位置是非常重要的,const将此直接记录在了代码中,编译器可以帮助我们实施这一点。正确编写const有助于更好地理解设计,使代码更牢固、更安全。如果发现有哪个成员函数不可能是const的,通常会使我们更好地理解成员函数修改对象状态的方式。还可以理解哪些数据成员在物理常量性和逻辑常量性之间架起了桥梁,下面的例子说明了这一点。
不要强制转换const,除非要调用常量不正确的函数,或者在一些很罕见的情况下,为了解决老编译器中不支持mutable的问题。
示例
例 在函数声明中,要避免将通过值传递的函数参数声明为const。以下两个声明是完全等效的:
void Fun( int x );
void Fun( const int x ); // 重新声明同一函数:顶级const将被忽略
在第二个声明中,const是多余的。我们推荐声明不含这些顶级const的函数,这样阅读头文件的人就不会被弄糊涂了。但是顶级const将对函数的定义产生影响,能够敏感地捕获对参数的无意改变:
void Fun( const int x ) { // Fun的实际定义
// ……
++x; // 错误:不能修改const的值
// ……
}





