条款26:尽可能延后变量定义式的出现时间
- 程序控制流到达变量定义式时,有构造成本;当变量离开作用域时,有析构成本;
- 减少无意义的default构造行为,定义一个变量的时候,最后给它一个初始值;
- 对于循环语句,除非明确1:赋值成本比构造析构成本低;2:正在处理代码中效率高度敏感的部分,使用方法A,否则使用方法B
class Widget;
//方法A:定义于循环外,代价一次构造,一次析构,n次赋值
//同时在该方法中w的析构被延迟了
Widget w;
for(int i = 0; i < n; ++i)
{
w = 取决于i的某个值;
}
方法B:定义于循环内,代价n次构造,n次析构
for(int i = 0; i < n; ++i)
{
Widget w(取决于i的某个值);
}
条款27:尽量少做转型动作
- const_cast用来去除对象的常量性
- dynamic_cast用来决定某对象是否归属继承体系中的某个类型(运行时判断)
- reinterpret_cast执行低级转型,实际实现与编译器相关(因此不可移植)
- static_cast强迫执行隐式转换(编译期判断)
- 如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_cast。如果有个设计需要转型动作,试着更换为无需转型的替代设计
- 如果转型必要,试着将它隐藏到某个函数后面,客户调用该函数接口,而无需将转型操作放进自己的代码中
条款28:避免返回handles(指针、引用和迭代器)指向对象内部成分
- 避免返回handles指向类对象的内部(不管返回参数是否用const修饰),因为一旦类对象提前被释放,那么返回的handles就悬空了
条款29:为“异常安全”而努力是值得的
- 异常安全函数需要满足的条件:1.不泄露任何资源;2.不允许数据败坏;
- 异常安全函数的级别:
- 基本承诺:如果异常被抛出,需要保证所有对象都保持在有效状态下,所有对象都处于内部前后一致的状态(所有有约束条件的对象在抛出异常前后,约束条件仍然满足,但不完全保证抛出异常后对象的状态与抛出异常前一致,也就是某些对象的状态可能发生变化了,只要所有对象的状态合法即可)
- 强烈保证:如果异常抛出,所有对象状态不改变。也就是一旦抛出异常,所有对象恢复到调用函数之前的状态(往往可以以copy-and-swap实现,但是“强烈保证”并非对所有函数都可实现或具有现实意义)
- 不抛出异常:意味着函数保证能够完成承诺的功能,但是不意味着函数不会抛出异常,此时一旦抛出异常,就意味着严重错误
- 函数提供“异常安全保证”的级别通常最高只等于该函数中所有调用函数的“异常安全保证”的最弱者(木桶原理)
条款30:透彻了解inlining的里里外外
- 将大多数inlining限制在小型、被频繁调用的函数身上
- 不要只因为function templates出现在头文件中,就将它们声明为inline
条款31:将文件间的编译依存关系降至最低
- 如果使用对象引用或对象指针可以完成任务,就不要使用对象本身(这样在头文件中只需要使用class声明类,而不需要包含该类的头文件)
- 如果能够,尽量以类声明替换类定义式
- 为声明式和定义式提供不同的头文件
- 两种手段Handle classes(1类提供实现,2类提供接口,在2类相关实现中调用1类的接口)和Interface classes(父类提供抽象接口,子类提供实现,同时父类提供工厂接口,这样可以通过父类的接口调用相关函数)
- 程序库头文件应该以“完全且仅有声明式”的形式存在