01 视c++为一个语言联邦
c++以c为基础, 对比c 多了面向对象(class,继承,多态,封装,虚函数等),模版template泛型编程,以及stl template 程序库。
02 尽量以const,enum,inline替代#define
宏定义只是简单替换,不做类型安全性检测,容易发生无法预知的错误。且define不重视作用域,无法定义一个class专属常量。
单纯常量用const或enum替换;而不想让别人获得一个指针或引用指向某个整数常量,enum可以实现这个约束。
函数用inline替换。
03 尽可能使用const
const在指针后限制指针为常量,可以修改指针指向的值;同理在指针前限制数据为常量,可以修改指针指向的地址。
const可以修饰class里的static ,non-static成员变量,函数;可以修饰class外全局或namespace作用域中的常量,修饰函数,文件,区块作用域中的对象。
函数返回一个常量值,可以减少调用者错误造成的意外,例如operater * 返回常量对象。
const成员函数:增强class接口可读性;使操作const对象成为可能。
当const和non-const成员函数有着实质等价实现时,令non-const版本调用const版本可避免代码重复。
04 确定对象被使用前已先被初始化
对象使用之前对其初始化,内置类型需手动初始化;
自定义对象初始化责任在构造函数;区分初始化和赋值。采用初始化值列表是初始化;赋值操作需要额外先调用类成员默认构造函数,再调用拷贝赋值操作,初始化值列表直接调用拷贝构造函数,效率更高。base class先于derived class初始化。
static对象初始化:
static对象全局定义、namespace、class、file作用域内为non-local static对象,函数内为local static对象。
定义于不同编译单元内的non-local static对象的初始化次序无明确定义;而这在有依赖关系的设计里可能出现未知问题;可以将每个non-local static对象搬到专属函数内,返回一个引用指向包含的对象,non-local static 被local static对象替换,单例模式实现。
05 c++默默编写了哪些函数
构造函数,析构函数,拷贝构造函数,拷贝赋值函数
拒绝为class生成拷贝赋值函数(类中有引用对象,常量对象);引用不可被修改,const对象不可被修改。
06 不想使用编译器自动生成的函数,就该明显拒绝
不支持拷贝,不需要生成拷贝构造函数和拷贝赋值函数。
自己定义二者为private 且没有定义(避免被成员函数或友元函数调用)或者继承uncopyable 基类。
07 为多态基类声明virtual 析构函数
派生对象由一个base class指针删除,如果base class没有virtual析构函数会发生未知的结果,派生对象析构函数没有被调用,而导致派生成分没有被销毁。产生局部资源泄露
只为多态基类声明virtual 析构函数,不是所有base class都声明virtual 析构函数。
08 别让异常逃离析构函数
析构函数里异常如果不处理,可能导致外层程序异常终止,而一些资源没有完全释放。解决方案,析构函数接受异常中止程序(abort),或者吞下异常。额外提供普通函数给客户调用执行(关闭等操作)
09 绝不在构造和析构过程中调用virtual函数
base class先于派生类构造,所以在base class构造函数里调用virtual函数是不会下降到derived class这层。base class构造期间,virtual函数不是virtual函数。
析构函数也是一样的,先进行derived class的析构,等到运行base class的析构函数时,derived class对象的成分已经呈现未定义值,所以进入base class的析构函数时被看待为base class的对象比较合理;包括virtual函数和dynamic_cast也是这样看待处理。
解决构造和析构函数调用virtual的方案,采取derived class向上传值。
10 令operator = 返回一个引用指向*this
支持实现赋值,为了和所有内置类型,stl提供的类型保持一致。
11 在operator =中处理自我赋值
由于别名的缘故,很可能出现自我赋值的情况出现。如果operator=里不处理自我赋值的情况,很可能把目标值删除,而出现持有一个已删除的指针而导致程序未知结果。
处理方案先判断是否自我赋值,而后处理;但是同样还需要考虑异常安全性,一般会采用复制原指针内存,new新内存,删除原指针内存。这样如果new 新内存失败 原指针指向内存仍被保留。还有一个copy and swap的方案。
12 复制对象时勿忘其每一个成分
主要是实现copy 构造函数和copy 赋值操作符,要确保新加的每一个成分都被拷贝。
保证每个local变量被copy;同时保证base class的每个成分被copy,调用base class适当的copying函数。
copy构造函数调用copy 赋值操作符无意义,等于给一个尚未构造好的对象赋值;
copy赋值操作符调用copy构造函数不合理,对一个已经存在的对象再次构造。
可以将copy构造函数和copy赋值操作符相同的部分提取一个新的init函数,供二者调用。