小结
- 本章是站在全局角度来看待的cpp,介绍了cpp是一个语言联邦,特性可能由于次语言不同而不同
- 尽量使用编译器而不是预处理器,使用constexpr、const、enum、inline来替代#define
- 确定对象被使用前已经被初始化,且注意non-local static多编译单元初始化顺序不确定性
条款 01:视 C++为一个语言联邦
- cpp内部包含四个次语言,c、object c++(也就是面向对象那一套)、template c++(泛型编程、模板元编程)以及STL
- 每个次语言的规则以及特性有所不同,比如c中内置类型pass-by-value比pass-by-reference更高效
所以并不是pass-by-reference永远是最优的,因为如果是传递的reference,就会涉及到一步"解引用",每次读取需要去解引用读取真实值,而对于"轻对象",直接传递值,操作就是直接对数据操作,且可能因为是在栈上而使用缓存加速机制
适合pass-by-value的情况:
不希望形态的改变影响实参
传递的是"轻对象",复制成本 ≤ 指针大小(如
int、double、迭代器、空函数对象)移动语义,就需要使用到移动拷贝构造,所以需要pass-by-value
迭代器,因为逻辑上等价于"指针",所以指针应该是使用pass-by-value;
- 不适合pass-by-value的情况:
- 需要使用多态,如果还是传value,则会出现对象切片
条款 02:尽量以const,enum, inline 替换 #define
define定义常量
- 可以使用
全局const常变量来代替,如#define PI 3.14 ==> int const Pi = 3.14,是该定义(包括指针常量)是可以出现在头文件的,因为此时变量没有共享性,但是如果又有extern修饰,就具有共享性,不能违反单一定义原则
constexpr是比const更严格的,后者声明的变量如果取地址则不能用来指定数组大小,而前者要求- 必须在编译期就能确定其值,且可用来指定函数,要求该函数在编译期也能调用
- 在类中,如果需要
#define定义的常量,则可以使用static const int n = 10;来代替,可以不在外面重新定义,此时编译器会把n作为编译时常量来对待,即可用于声明数组个数 - enum hack ,一个属于枚举类型的数值可权充int使用,这样可以实现一个专属于某一个类的编译器常量

define定义函数
- 即使加上了括号,如果传入i++基本都会出问题
- 使用inline+template来替代宏函数
条款 03:尽可能使用const
const与对象
const A a;,对象a直接访问非mutable成员数据修改或调用成员方法修改,都是不行的- 调用的成员方法是const修饰的,否则会报错
const与迭代器
const vector<int>::iterator iter = vec.begin(),声明iter本身是不变的,const_iterator iter是声明iter指向的对象不可变化
const与函数声明
- const与返回值,比如
operator*()最好返回const,避免用户代码if((a*b)=c) - const与参数,如果内部不需要修改该参数,尽量使用const修饰
const与成员函数
- 原因:外部如果是const声明的对象,调用的方法就是const修饰的,const成员变量是声明不会改变对象内部非
mutable数据成员的 - 其实const修饰的是参数this
- 同时const修饰底层指针,会构成重载,non-const优先调用non-const成员函数,const只能调用const成员函数,也就是成员函数有const修饰会重载
- bitwise const是对象占据的内存值不会发生改变,cpp中对于const就是这样想的
- logic const 是能修改
mutable修饰的成员 - 尽量使用non-const调用const成员函数,来减少重复代码
条款 04:确定对象被使用前已先被初始化
- c-part-of-cpp 是不会初始化的(比如array),而其他部分是会初始化数据的(比如vector)
- 对象数据成员的初始化发生在进入构造函数本体之前,构造函数本体都是赋值
- 在初始化列表中列出所有数据成员
- static变量是存储在数据区,而不是heap、stack
local static object是指函数中声明定义的static变量,会在程序启动时就在数据区分配空间,初次运行到定义处进行初始化,线程安全non-local static object,包含全局static、类中static以及命名空间内的static,在main运行前初始化,但是存在跨编译单元初始化顺序不确定问题,解决方案是用local static替换non-local static