条款1:视C++为一个语言联邦


条款2尽量以 const,enum,inline 替换 #define


条款3:尽可能使用 const

  • bitwise constness(physics constness):不更改对象内的任何一个 bit

但是对于指针对象等等,在这一派的思想中,只有指针属于对象,而“指针所指物”不属于对象

  • logical constness:一个 const 成员函数可以修改它所处理的对象内的某些 bits,但只有在客户端侦测不出的情况下才可以。

关键字 mutable 释放掉 non-static 成员变量的 bitwise constness 约束。

运用 const 成员函数实现其 non-const 孪生兄弟”的技术,反向则不可用。
为了减少一些重复的代码,可以通过 const 函数来实现 non-const 函数。这时需要常量性移除(casting away constness)。

  1. class TextBlock {
  2. public:
  3. // ...
  4. const char& operator[](std::size_t position) const {
  5. // ... 其他操作
  6. return text[position];
  7. }
  8. char& operator[](std::size_t position) { // 通过调用 const op[] 来实现 non-const op[]
  9. return
  10. const_cast<char&>( // 移除 op[] 返回值的 const
  11. static_cast<const TextBlock&>(*this) // 为 *this 加上 const
  12. [position] // 调用 op[]
  13. );
  14. }
  15. }

需要 2 个转型动作。因为 non-const operator[] 内部单纯调用 operator[] ,会递归的调用自己。所以先加上 const 属性,使其调用 const operator[]。


条款4:确定对象被使用前已先被初始化

读取未初始化的值会导致不明确的行为。
永远在使用对象之前,先将它初始化。

对于内置类型以外的任何其他东西(classstruct),确保每一个构造函数都将对象的每一个成员初始化

比如 struct 也写一个构造函数,并且进行初始化。

不要混淆了赋值(assignment)和初始化(initialization)的概念!

初始化发生在进入构造函数之前!

  1. // 假设 Base() 是一个构造函数
  2. Base(int x) {
  3. this->theX = x; // 这是赋值
  4. }
  5. Base(int x): theX(x) {} // 这是初始化

上述的两种写法中,

  1. 第一种会先调用 default 构造函数初始化变量 theX,然后将 x 赋值给 theX。那么 default 构造函数做的事情都浪费了。
  2. 第二种直接 theX 以 x 为初值进行 copy 构造,更高效。

总是在初值列中列出所有成员变量,以免还要记住遗漏了哪些成员变量。
对于成员变量

  • 内置对象,可以指定 nothing 作为初始化实参
  • 自定义对象,则明确指出。
    1. Base()
    2. :theX(), // int,内置对象
    3. myStruct(2) // 自定义的 struct
    4. {}

如果成员变量是 constreferences,它们就一定需要初值,不能被赋值。

classes 拥有多个构造函数,每个构造函数有自己得成员初值列。那么,可以合理地在初值列中遗漏那些“赋值表现像初始化一样好的”成员变量,改用它们的赋值操作,并将那些赋值操作移往某个函数(通常是private),供所有构造函数调用。

C++有固定的“成员初始化次序”:base classes 早于 derived classes,class 的成员变量总是以其声明次序被初始化(与成员初值列的次序无关)。

C++对“定义于不同编译单元内的 non-local static 对象”的初始化次序并无明确定义。

编译单元(tranlation unit)指产出单一目标文件(single object file )的那些源码,基本上是单一源码文件加上其所含入的头文件(#include)

如何消除这个问题:将每个non-local static 对象搬到自己的专属函数内(该对象在此函数内被声明为 static)。这些函数返回一个 reference 指向它所含的对象。然后用户调用这些函数,而不直接指涉这些对象。
就是 Singleton 模式的常见实现方法。

总结:

  • 为内置型对象进行手工初始化,因为C++不保证初始化它们。
  • 构造函数最好使用成员初值列(member initialization list),而不要在构造函数本体内使用赋值操作(assignment))。初值列列出的成员变量,其排列次序应该和它们在 class 中的声明次序相同。
  • 为免除“跨编译单元之初始化次序”问题,请以**local static** 对象替换 non-local static 对象。

条款5:了解C++默默编写并调用哪些函数

对一个空类,编译器会声明,

  • copy 构造函数
  • copy assignment 操作符
  • 析构函数
  • default 构造函数

这些函数都是 public 且 inline。
只有这些函数被调用时,会被编译器创建。


条款6:


条款7:为多态基类声明virtual析构函数