条款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)。
class TextBlock {public:// ...const char& operator[](std::size_t position) const {// ... 其他操作return text[position];}char& operator[](std::size_t position) { // 通过调用 const op[] 来实现 non-const op[]returnconst_cast<char&>( // 移除 op[] 返回值的 conststatic_cast<const TextBlock&>(*this) // 为 *this 加上 const[position] // 调用 op[]);}}
需要 2 个转型动作。因为 non-const operator[] 内部单纯调用 operator[] ,会递归的调用自己。所以先加上 const 属性,使其调用 const operator[]。
条款4:确定对象被使用前已先被初始化
读取未初始化的值会导致不明确的行为。
永远在使用对象之前,先将它初始化。
对于内置类型以外的任何其他东西(class、struct),确保每一个构造函数都将对象的每一个成员初始化。
比如 struct 也写一个构造函数,并且进行初始化。
不要混淆了赋值(assignment)和初始化(initialization)的概念!
初始化发生在进入构造函数之前!
// 假设 Base() 是一个构造函数Base(int x) {this->theX = x; // 这是赋值}Base(int x): theX(x) {} // 这是初始化
上述的两种写法中,
- 第一种会先调用 default 构造函数初始化变量 theX,然后将 x 赋值给 theX。那么 default 构造函数做的事情都浪费了。
- 第二种直接 theX 以 x 为初值进行 copy 构造,更高效。
总是在初值列中列出所有成员变量,以免还要记住遗漏了哪些成员变量。
对于成员变量
- 内置对象,可以指定 nothing 作为初始化实参
- 自定义对象,则明确指出。
Base():theX(), // int,内置对象myStruct(2) // 自定义的 struct{}
如果成员变量是 const 或 references,它们就一定需要初值,不能被赋值。
若 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。
只有这些函数被调用时,才会被编译器创建。
