Item03_const
只要明确值不被改变就应该声明为 const
,以此获得编译器的襄助,保证规则不被违反。
顶层 const
与 底层 const
const pointer 成为顶层 const, pointer 指向的数据为 const 称为底层 const.
1 | char greeting[] = "hello world"; // greeting 类型为 char [12] |
底层 const 的以下两种写法等价
1 | const char *p; |
STL 中的 iterator 类似于 (T*) 指针。声明迭代器为 const 类似于 T * const
,顶层const。而如果需要迭代器所指向的数据不被修改,需要 const_iterator
。
1 | std::vector<int> v{1,2,3}; |
函数返回值
将函数范围值声明为 const 可以减少不必要的麻烦,比如
1 | class Rational { |
如果去掉返回值的 const ,你的用户可能做出如下行为
1 | (3*5) = 3 |
你可能觉得根本不会有人做出怎么奇怪的操作,但是下面这个错误就比较隐蔽, 用户在条件语句中将 ==
写成了 =
,发生隐式 bool 类型的转化:
1 | if( (3*5) = 3) |
尽量将使用 reference-to-const 作为参数不仅避免拷贝,还能绑定到右值,比如:
1 | void foo (std::string & s) |
尽管 const 只有六个字符,但是却能避免很多不必要的麻烦。
const 成员函数
目的
- 使得 class 接口容易被理解,明白哪些函数改动内容,而哪些不行
- 使得 ‘操作 const 对象’ 成为可能。因为很多情况需要使用 pass by reference-to-const ,这项技术的前提就是需要 const 成员函数。
1 | class TextBlock |
下面是调用 operator [] 的例子
1 | void print(const TextBlock & str) { //str 是 const 对象 |
如果 operator [] 的返回值类型是 char reference ,如果是 char 的话,下面句子无法编译:
1 | str[0] = 'x' |
bitwise const and logical const
- bitwise const: class 内部的成员变量不能修改 ,编译器检测的方式
- logical const: 设计者期望的常量
1 | class CTextBlock |
这里我们想要的 const 是 p 的内部值不被改变,但是 operator[] 返回值没加 const 时也能编译成功,即逃过了编译器 bitwise const 检查。所以尽可能使用 STL 中的 string 而非手动管理内存。
下面在介绍一个 logical const 被 编译器 bitwise const 所过度限制的例子。
1 | class CTextBlock |
显然 length()
不是 bitwise const
, 因为 textlength
发生了改变。但是对于我们而言,textlength
与 lengthIsvalid
是可以接受的。 我们可以使用 mutable
关键字释放掉 non-static 成员变量 bitwise const 的约束。
1 | class CTextBlock |
在 const 成员函数和 non-const 成员函数中避免重复
在对某个函数实现 const 与 non-const 重载时,会产生大量的代码重复,比如在上面的例子中放入更多复杂的操作
1 | class TextBlock |
我们应该考虑令其中的一个调用另一个,这是一种很重要的思想,在重载 +=
和 +
是也利用之。利用 const 成员函数实现 non-const
1 | class TextBlock |
这里用到两次转型 分别是为 *this
添加 const , 为 op[ ] 的返回值移除 const. 注意 const_cast
与 static_const
的区别。只有 const_cast
能移除 const。
但是反向做法:利用 non-const 成员函数实现 const 是不正确的。这是一个危险的行为,首先 non-const 函数并未声明不对对象进行修改,而 const 成员函数依赖与一个改变对象的函数显然是不合理的。而且你不得不使用 const_cast
去除 const,最终实现的 const 成员函数不能保证真正的 const. const 成员函数声明也就失效了。
1 | class TextBlock |
总结
- 将某些东西声明为 const 可帮助编译器侦测错误用法。const 可作用于对象,函数参数,函数返回值类型,成员函数本体。
- 编译器实施 bitwise constness ,但编写程序时应使用 conceptual constness.
- non-const 版本调用 const 版本避免代码重复
参考
Effective C++ 中文版(第三版)