1. const 的基本作用与特性
- 语义约束:const 能指定对象不应被修改这一语义约束,编译器会执行该约束,可借助它减少因意外修改值而产生的错误,同时向编译器和其他程序员表明某个值应保持不变。
- 应用范围广泛:在类的外部,可用于全局、命名空间范围的常量,以及文件、函数、模块范围内声明为静态的对象;在类内部,可用于静态和非静态数据成员;对于指针,能指定指针本身、所指数据或者两者是否为常量等多种情况。
- 语法规则:const 出现在星号左边,表示指针指向的内容为常量;出现在星号右边,表示指针自身为常量;出现在两边,则两者都是常量。对于函数参数中 const 放在类型之前或之后星号之前,意义无区别。
2. const 在不同场景中的应用及影响
- 迭代器方面:声明迭代器为 const 类似于声明指针为 const,即不能改变迭代器的指向,但指向的内容可变化;若要迭代器指向不能变化的内容,需使用 const_iterator。
- 函数声明方面:
- 返回值为 const:函数返回常量值可减少客户错误造成的影响,如将自定义类型的操作符重载函数(如 operator*)的返回值声明为 const,能避免对运算结果进行不合理赋值的情况。
- 参数为 const:其行为类似局部的 const 对象,在不需要改变参数或本地对象时,应将其声明为 const,有助于避免因误操作(如将 “==” 写成 “=”)带来的错误。
- const 成员函数:被声明为 const 的成员函数可被 const 对象调用,这使类的接口更易理解,且利于以传引用给 const 的方式传递对象来提升程序性能;成员函数可基于常量性不同进行重载,不同版本的重载函数能针对 const 和非 const 对象进行不同操作。
3. const 相关的两种常量性概念
- 二进制位常量性(bitwise constness):认为成员函数只要不改变对象的非静态数据成员(即对象内的二进制位)就是 const,编译器易监测其违例,但存在局限性,如某些改变指针指向内容的函数虽能通过该检验,但实际效果并非真正的 const。
- 逻辑常量性(logical constness):主张 const 成员函数可在客户无法察觉的情况下改变对象中的一些二进制位,但编译器默认遵循二进制位常量性,对于逻辑常量性相关情况不认可。可利用 mutable 关键字将非静态数据成员从二进制位常量性约束中解放出来,使其能在 const 成员函数中被修改。
4. 避免 const 与非 const 成员函数重复代码的方法
- 利用强制转型:当 const 和非 const 成员函数实现基本相同时,为避免代码重复,可让非 const 版本调用 const 版本,通过适当的强制转型(如先使用 static_cast 为 this 指针加上 const 以调用 const 版本的函数,再用 const_cast 去掉 const 版本函数返回值的 const 属性)来实现,不过一般情况下应避免强制转型,仅在此种特殊场景下为避免重复代码可采用;反向地从 const 成员函数调用非 const 成员函数是错误的,因为可能改变本应保持不变的对象状态。
5. 总结要点
- 将相关内容声明为 const 有助于编译器发现使用错误,其能应用于多种范围的对象、函数参数、返回类型以及成员函数等方面。
- 编程时应从概念上的常量性(conceptual constness,即 logical constness)角度考虑,尽管编译器坚持二进制位常量性。
- 当 const 和非 const 成员函数有相似实现时,使用非 const 版本调用 const 版本可避免代码重复。