一构造/析构/复制
catch5 了解C++默认操作
5.1 编译器对空类class E{};自动编写一个默认构造函数E();一个拷贝构造函数E(const E& rhs){}一个析构函数~E(){}一个拷贝赋值操作符 E& operator =(const E& rhs){}
当E()内含引用成员进行赋值操作时,应该在初始化列表初始化,编译器不会为其生成copy assignment操作符;如果基类函数将copy assignment操作符声明为私有函数,同样滴子类也不会默认生成。
catch6 拒绝编译器自动生成函数的两种方法
将构造函数私有化且只声明不实现;或者继承自一个设计好的基类;
catch7 多态基类的析构函数写成virtual函数
一个类至少含有一个virtual函数时将其析构函数写成virtual函数,避免被继承时子类对象调用父类虚函数而形成“局部销毁”导致的内存泄漏;
但同时一个类不是为了作为基类或者实现多态,就不必把虚构函数声明为虚函数;
catch8 析构函数绝不吐出异常
catch 9 绝不在构造/析构过程中调用(基类和派生类共有的)virtual函数,构造函数调用的所有函数也同时服从这个约束。
这样会导致子类对象初始化时先调用父类构造初始化的是父类虚函数,子类虚函数不被构造函数初始化。对象内部含有未初始化的 成员,析构函数也同样如此
catch 10 令operator = 返回一个this*指针以实现连锁赋值
catch 11 operator =处理自我赋值
catch 12 拷贝构造和operator =自行编写时不要放过类的每个成员的覆盖,包含基类的成员
二 资源管理
catch 13 以对象管理资源 (用于代替)
资源管理时使用RAII对象,他们在构造函数中获取资源并在析构函数中释放资源,以防止动态分配的对象在delete环节被横插一杠,导致内存泄漏,确保大部分申请的资源总是能被释放。常用的RAII有auto_ptr 以及shared_ptr(智能指针)
catch 15 资源管理类对原始资源的访问(显式转换和隐式转换):get()
catch 16 new和delete形式统一
new一个空间时new std::string与new std::string[]对应要保证delete和delete []保持高度统一;同样的typedef的new也要用对应delete形式(或者可以不要常用typedef数组以免自己delete的时候忘记)
三 设计与声明:考虑class设计的细节
catch 18 避免接口被客户错误使用:接口一致性,内置行为兼容;
catch 19 像type一样去设计类
catch 20 以const reference传递常引用代替value传递:value传递本质是由copy构造函数实现的,在某些方面效率不高,这些规则不适用于内置类型以及STL 迭代器和函数对象
catch 21 函数返回值:用返回对象本身去代替 返回指针或者引用
catch 22 成员变量尽量用private替代protected和public:public意味着不封装,不封装几乎意味着不可改变;而protected的封装性不比public好
catch 23 可考虑用非成员 非友元函数替代成员函数 减少多个成员函数对私有数据的访问,加强封装性,包裹弹性和机能扩充
catch 25 考虑写一个不抛出异常的swap函数
四 实现
catch 26 延缓变量定义式出现位置,直到该变量能够有初始值可以做初始实参,避免不必要的构造/析构/默认构造函数
catch 27 少写转型
catch 28 避免返回handles(别名 指针 迭代器)指向对象内部数据(破坏封装)
catch 29 努力做到异常安全
catch 30 inline的全面理解:先不要随意将任意函数列为inline函数,除非确定它小型,频繁调用,不会做任何改动的基础上
(重点掌握)catch 31将文件的编译依存关系降到最低
1.将对象实现细节隐藏到一个指针背后:将其分割为两部分,一个只提供接口,一个负责实现接口。这种接口与实现分离的称为handle classes,handle classes实现两种形式:
1.1 为声明和实现提供不同的头文件,将定义类的所有函数交给实现类implement class并由后者完成实际工作
1.2 令handle class成为特殊的抽象基类 用具体实现去继承并实现细节
五 OOP设计和继承
catch 32 public继承要明确为is-a关系:实现子类对父类的一种特殊化,实现里氏替换原则(基类可被子类替代)public=is a;virtual= 接口必须被继承;non virtual=接口和实现必须被继承
catch 33 避免遮盖继承来的名字:作用域覆盖:内层作用域会覆盖外层,没有实现virtual关键字之前,子类继承时会将继承来的函数名覆盖掉
catch 34 区分接口继承和实现继承(类似于声明继承和定义继承)
1.纯虚函数 pure virtual:必须被继承类重新声明,且在基类中通常不被定义
catch 35 考虑virtual继承以外的其他选择
NVI手法:Non-Virtual Interface 令用户通过public nv成员访问private virtual函数,让子类重新定义(决定如何完成)自己可能并不调用(决定实现顺序)的函数,让基类保留合适调用的权利。 被访问的private virtual函数不一定写成私有,必要时可以写成protected(Template Method)
catch 36 绝不重定义继承而来的非虚函数
catch 37 绝不重定义继承而来的缺省参数值
catch 39 private继承
1.private继承两大准则:编译器不会将派生类对象自动转换成基类对象;私有继承来的所有成员在派生类中全为私有属性(无论之前在基类中是什么)
catch 40 多重继承