📘【EffectiveC++】Chapter1-让自己习惯C++
1. 视C++ 为一个语言联邦
C++并不是某种特定语言,应该把它视为以下四种part of C语言的集合:
- C
- Object-Oriented C++
- Template C++
- STL
C++ 的高效编程指南会视情况而变化,取决于你使用了以上四个的哪一部分。
2. 尽量以const,enum,inline替换#define
核心原因
#define
无视作用域且不进入符号表,导致调试困难且破坏封装性。
原因解释
#define
被作用于预处理阶段,它可能从未被编译器看到。所以自然就不会进入记号表内。这会导致当你定义了以下一个宏:
#define ACCD_BG 1.62
时,如果发生了某些编译错误,你只会在编译信息里看到 1.62
而不是 ACCD_BG
#define
无法提供封装性,因为它并不重视作用域。
一旦宏被定义,它就在其后的编译过程中有效,除非在某处被#undef
其他
- string对象通常比char* 更合适。
- 提到的enum hack方法,在C++11+项目应使用[[constexpr]]
- C++一般总是要求你对使用的任何东西提供一个定义式。
3. 尽可能使用const
核心原因
const成员函数承诺不修改对象状态,但通过指针仍可能修改数据(如获取成员地址后修改)。mutable修饰符允许在const成员函数中修改特定成员,实现逻辑const与物理const的分离。
原因解释
const修饰成员函数的一大作用是保证这个函数不会修改类的非静态成员。
但是存在一个问题,即如果声明一个非const的指针来获取到了某个const修饰的成员地址。那你其实是可以通过这个指针来改变这片内存的。如果你用const A a
, 且调用一个const修饰的成员函数,但你最终却还是通过一个指针p来改变了a的值。
即这种const成员函数的保证其实是不完全可靠的。
所以这里存在另外一种思维主张,即:即使是const修饰的成员函数,它也可以修改类成员的某些bit。你需要为这些可能被修改的成员前添加mutable
修饰。
最佳实践
- 当你可以用const,请用
- 对于那些可能被修改的成员,请添加
mutable
来显式修饰。
4. 确保对象在使用前已经先被初始化
核心原因
C++并没有在所有语境保证会把你定义的对象进行默认初始化。 初始化列表避免伪初始化开销。
原因解释
1
int x; 有时候它会被初始化为0,有时候并不会。
2
在构造函数中赋值的方法实际上是一种伪初始化,对象被实际调用default构造函数的时间比进入类的构造函数更早。 对于内置对象(如int)则不一定,它不保证在构造函数之前一定被赋初值。 所以如果使用在构造函数中赋值的方式, default构造函数所做的一切其实被浪费了。 故初始化列表是一种更高效的方式。
最佳实践
- 永远在使用对象之前先把它初始化。
- 对于类的构造函数初始化,应该使用初始化列表的方式,而不是在构造函数中去给成员赋值。