条款01:视C++为一个语言联邦
C++是个多重范型编程语言,一个同时支持面向过程形式、面向对象形式、函数形式、泛型形式、元编程形式的寓言。
将C++视为几个子语言:
传统C:区块、语句、预处理器、内置数据类型、数组、指针。没有模板、没有异常、没有重载。
面向对象C++:类(包括构造函数析构函数)、封装、继承、多态、虚函数。
Template C++:泛型编程、模板元编程。
STL:容器、迭代器、算法、函数对象(仿函数)。
记住:
C++高效编程守则视状况而变化,取决于你使用C++的哪一部分。
条款02:尽量以const,enum,inline替换#define
用编译器代替预处理器。#define不被视为语言的一部分,所使用的名称不进入记号表。
1 #define ASPECT_RATIO 1.653 2 const double AspectRatio = 1.653; 3 const char* const authorName = "Scott Meyers"; 4 const std::string authorName2("Scott Meyers"); 5 class GamePlayer { 6 static const int NumTurns = 5; 7 enum { NumTurns2 = 5 };// 一个属于枚举类型的数值可以当做int使用 8 int scores[NumTurns]; 9 }; 10 const int GamePlayer::NumTurns;// 需要取地址时不能省略 11 #define CALL_WITH_MAX(a, b) f((a)>(b)?(a):(b)) 12 template<typename T> 13 inline void callWithMax(const T& a, const T& b) { 14 f(a>b?a:b); 15 }
记住:
对于单纯常量,最好以const对象或enums替换#defines。
对于形似函数的宏,最好改用inline函数替换#defines。
条款03:尽可能使用const
const 允许指定一个不能被改动的约束,而编译器会强制实施这项约束。
const 可以在 classes 外部修饰 global 或 namespace 作用域中的常量,或修饰文件、函数或区块作用于中被声明为 static 的对象。也可以修饰 classes 内部的 static 和 non-static 成员变量。面对指针,也可以指出指针自身、指针所指物或两者都是 const:如果关键字 const 出现在星号左边,表示被指物是常量;出现在星号右边,表示指针自身是常量;出现在星号两边,表示被指物和指针两者都是常量。
STL迭代器声明为 const 相当声明指针为 const,迭代器不得指向不同的东西,但它所指的值是可以改动的。const_iterator 所指的东西不可改动。
在函数声明式内,const 可以和函数返回值、各参数、函数自身(成员函数)产生关联。令函数返回一个常量值,可以避免客户错误造成的意外。将 const 实施于成员函数的目的,是为了确认该成员函数可作用于const对象身上:第一,它使 class 接口比较容易理解;第二,它使操作 const 对象成为可能,以 pass by reference-to-const 方式传递对象。两个成员函数如果只是常量性不同可以被重载。
一个const成员函数可以修改它处理的对象内的某些数据,但只有在客户端侦测不出的情况下才可以,例如高速缓存的文本区块的长度等。mutable 关键字修饰的成员可以被更改。
运用 const 成员函数实现出其 non-const 孪生兄弟来避免代码重复。
记住:
将某些东西声明为 const 可帮助编译器侦测出错误用法。const 可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。
编译器强制实施 bitwise constness,但你编写程序时应该使用 conceptual constness(概念上的常量性)。
条款04:确定对象被使用前已先被初始化
static 对象,其寿命从被构造出来直到程序结束为止,因此 stack 和 heap-based 对象都被排除。这种对象包括 global 对象、定义于 namespace 作用域内的对象、在 classes 内、在函数内、以及在 file 作用域内被声明为 static 的对象。函数内的 static 对象称为 local static 对象,其他 static 对象称为 non-local static 对象。程序结束时 static 对象会被自动销毁,它们的析构函数会在 main() 结束时被自动调用。
编译单元是指产出单一目标文件的那些源码。基本上是单一源码文件加上其所含入的头文件。
多个编译单元内的 non-local static 对象经由模板隐式具现化形成,不能决定正确的初始化次序。
将每个 non-local static 对象搬到自己的专属函数内,该对象在此函数内被声明为 static。这些函数返回一个指向他所含对象的引用,non-local static 对象被 local static 对象替换了。这是单例模式的一个常见实现手法。
C++保证,函数内的 local static 对象会在该函数被调用期间、首次遇上该对象的定义式时被初始化。
记住:
为内置型对象进行手工初始化,因为C++不保证初始化它们。
构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列出的成员,其排列次序应该和它们在class中的声明次序相同。
为避免跨编译单元的初始化次序问题,以 local static 对象替换 non-local static 对象。