条款 03:尽可能使用 const

Use const whenever possible

const 修饰指针和常量

  1. char greeting[] = "Hello";
  2. char* p = greeting; // non-const pointer, non-const data
  3. const char* p = greeting; // non-const pointer, const data
  4. char const * p = greeting; // non-const pointer, const data
  5. char* const p = greeting; // const pointer, non-const data
  6. const char* const p = greeting; // const pointer, const data

const 修饰迭代器

  1. std::vector<int> vec;
  2. const std::vector<int>::iterator it = vec.begin(); // 效果等同 T* const
  3. *it = 10; // 可以改变成功
  4. ++it; // 错误
  5. std::vector<int>::const_iterator it = vec.begin(); // 效果等同 const T*
  6. *it = 10; // 错误
  7. ++it; // 可以迭代成功

const 修饰函数返回值

  1. class Rational {...};
  2. const Rational operator* (const Rational& lhs, const Rational& rhs);

返回 const 能够避免下面这种错误

  1. Rational a, b, c;
  2. (a * b) = c; // 错误,可能不是预期的

const 成员函数

  • 能够使 class 接口更加清晰,得知哪个函数会改动对象而哪个不会
  • 使 pass by reference-to-const 成为可能

const 重载

两个成员函数如果只是常量性不同,可以被重载

  1. class TextBlock {
  2. public:
  3. const char& operator[] (std::size_t) const { return text[position]; } // operator[] for const 对象
  4. char& operator[] (std::size_t) { return text[position]; } // operator[] for non-const 对象
  5. private:
  6. std::string text;
  7. };
  8. TextBlock tb("Hello");
  9. const TextBlock ctb("Hello");
  10. tb[0] = 'x'; // 没问题
  11. ctb[0] = 'x'; // 错误

bitwise constness 和 logical constness

主张 bitwise constness 的人认为,一旦成员函数被申明为 const,就不能改变任何成员变量(除 static),即不更改任意一个 bit。然后这种观念很容易出现不合理的地方,比如某个对象内包含一个指针,某个 const 成员函数未改变指针但是改变了指针所指的值,虽然符合 bitwise constness,但是导致了反直观效果。
主张 logical constness 的人认为,允许 const 成员函数改变某些 bits,但是应该不被客户端感知,或者说符合预期。如下面获取一个高速缓存的文本区块的长度,其中 textLength 和 lengthIsValid 会实时变更,且符合预期,则可以使用 mutable 关键字申明可变。

  1. class CTextBlock {
  2. public:
  3. std::size_t length() const;
  4. private:
  5. char* pText;
  6. mutable std::size_t textLength;
  7. mutable bool lengthIsValid;
  8. };
  9. std::size_t CTextBlock::length const
  10. {
  11. if (!lengthIsValid) {
  12. textLength = std::strlen(pText);
  13. lengthIsValid = true;
  14. }
  15. return textLength;
  16. }

当实现等价时,重载时以 non-const 调用 const 避免重复

如果实现 const 和 non-const 两个版本的函数,代码会重复冗长,可以重载时以 non-const 调用 const 避免重复,反向则不行。

  1. class CTextBlock {
  2. public:
  3. const char& operator[] (std::size_t) const { return text[position]; }
  4. char& operator[] (std::size_t) {
  5. return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
  6. }
  7. private:
  8. std::string text;
  9. };

static_cast 将传入参数转为 const TextBlock&,const_cast 将返回值去 const。如果 non-const 版本实现中需要改变成员变量,则不适用。