Effective C++ 学习笔记(二)
- 总共54个条款,分为9个 部分
- 让自己习惯C++
- 构造/析构/赋值运算
- 资源管理
- 设计与声明
- 实现
- 继承与面向对象
- 模板与泛型编程
- 定值new和delete
- 杂项讨论
设计与声明
- 让接口容易被正确使用,不易被误用
- 设计class犹如设计type
- 新type的对象应该如何被创建和销毁?
- 构造函数
- 析构函数
- 内存分配和释放函数operator new、operator new[]、operator delete、operator delete[]
- 对象的初始化和对象的赋值该有什么样的差异?
- 构造函数
- 赋值操作符
- 新type的对象如果被passed by value,意味着什么?
- copy构造函数用来定义一个type的pass-by-value该如何实现
- 什么是新type的“合法值”?
- 对成员函数必须进行的错误检查工作
- 抛出的异常
- 函数异常明细列表
- 新的type需要配合某个继承图系?
- 新type需要什么样的转换?
- 隐式和显式转换函数
- 什么样的操作符和函数对此新type而言是合理的?
- 什么样的标准函数应该驳回?
-被声明为private的部分 - 谁该取用新type成员?
- 什么是新type的“未声明接口”?
- 新type有多么一般化?
- 是否真的需要新的type?
- 新type的对象应该如何被创建和销毁?
- 宁以pass-by-reference-to-const替换pass-by-value
- 尽量以pass-by-reference-to-const替换pass-by-value。前者通常比较搞笑,并可避免切割问题。
- 以上规则并不适用于内置类型,以及STL的迭代器和函数对象。对他们而言,pass-by-value往往比较适当。
- 必须返回对象时,不要返回reference
绝对不要返回pointer或reference指向一个local stack对象,或返回reference指向一个heap-allocated对象,或返回pointer或reference指向一个local static对象而言有可能同时需要多个这样的对象。 - 将成员变量声明为private
- 以non-member 和 non-friend 函数替换member 函数
- 如果需要为某个函数的所有参数(包括this指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是个non-member。
实现
- 尽量延后变量定义式的出现时间
- 尽量少做转型动作
- const_cast
- dynamic_cast
- reinterpret_cast
- static_cast
- 确保异常安全
- 任何使用动态内存的东西(如STL的容器)如果无法找到足够内存以满足需求,通常便会跑出一个bad_alloc异常。
- Inline函数
- 将文件的编译依存关系降至最低
- 支持“编译依存性最小化”的一般构想是:相依于声明式,不要相依与定义式。基于此构想的两个手段是Handle classes和Interface classes。
- 程序库头文件应该以“完全且仅有声明式”(full and declaration-only forms)的形式存在。这种做法不论是否设计templates都是用。
继承与面向对象设计
- 确定public继承塑模出is-a关系
public继承意味着适用于基类身上的每一件事情一定也适用于继承类身上,因为每一个继承类对象也都是一个基类对象。 - 避免遮掩继承而来的名称
- derived classes内的名称会遮掩base classes内的名称。在public继承下从来没有人希望如此。
- 为了让被遮掩的名称再见天日,可使用using声明式或inline转交函数(forwarding functions)。
- 区分接口继承和实现继承
- 接口继承和实现继承不同。在public继承之下,derived classes总是继承base class的接口。
- 纯虚函数只具体指定接口继承。
- 非纯虚函数具体制定接口继承及缺省实现继承。
- 非虚函数具体指定接口继承以及强制性实现继承。
- 考虑virtual函数以外的其他选择
- 籍由Non-Virtual Interface手法实现Template Method设计模式
- 籍由Function Pointers实现Strategy设计模式
- 籍由tr1::function完成Strategy设计模式
- 使用NVI手法,那是Template 设计模式的一种特殊形式。它以public non-virtual成员函数包裹较低访问性(private或protected)的virtual函数。
- 将virtual函数替换为“函数指针成员变量”,这是Strategy模式的一种分解表现形式。
- 以tr1::function成员变量替换virtual函数,因为允许任何可调用物(callable entity)搭配一个兼容于需求的签名式。这也是Strategy模式的某种形式。
- 将继承体系内的virtual函数替换为另一个继承体系内的virtual函数。这是Strategy模式的传统实现手法。
- 绝不重新定义继承而来的non-virtual函数
- 绝不重新定义继承一个带有缺省值的virtual函数
- 通过复合塑模出has-a或“根据某物实现出”
- 明智而审慎地使用private继承
- 明智而审慎地使用多继承(MI)
- 多重继承比单一继承复杂。他可能导致新的歧义性,以及对virtual集成的需要。
- virtual继承会增加大小、速度、初始化(及赋值)复杂度等等成本。如果virtual base classes不带任何数据,将是最具实用价值的情况。
- 多长继承的确有正当用途。其中一个情节设计“public继承某个Interface class”和“private继承某个协助实现的class”的两相组合。