Effective C++笔记(五):实现
参考:http://www.cnblogs.com/ronny/p/3754755.html
条款26:尽可能延后变量定义式的出现时间
有些对象,你可能过早的定义它,而在代码执行的过程中发生了导常,造成了开始定义的对象并没有被使用,而付出了构造成本来析构成本。
所以我们应该在定义对象时,尽可能的延后,甚至直到非得使用该变量前一刻为止,应该尝试延后这份定义直到能够给它初值实参为止。
这样做的好处是:不仅可以避免构造(析构)非必要对象,还可以避免无意义的default构造行为。
遇到循环怎么办?此时往往我们会有两个选择:
做法A:1个构造函数+1个析构函数+n个赋值操作 // 在循环外面定变量,在循环内赋值
做法B:n个构造函数+n个析构函数 // 在循环内定义并初始化变量
这时候要估计赋值的成本低还是构造+析构的成本低,另外值得考虑的是对象作用域的问题。除非n特别大,否则优先选择做法B
定义时变量时直接拷贝要好于先用默认构造函数定义,再进行赋值。
条款27:尽量少做转型动作
转型语法通常有三种不同的形式:
1,C风格的转型动作:(T)expression
2,函数风格的转型动作:T(expression)
3,上面的第二种被称为“旧式转型”,C++提供了四种新式转型:
const_cast<T>(expression) //通常用来将对象的常量性转除
dynamic_cast<T>(expression) // 转换为子类,用来决定某对象是否归属继承体系中的某个类型,但是耗费重大运行成本
reinterpret_cast<T>(expression) // 执行低级转型,实际动作取决于编译器,这也就表示它不可移植。如将一个point to int 转型为一个int
static_cast<T>(expression) // 强迫隐式转换
请记住:
如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_cast。如果有个设计需要转型动作,试着发展无需转型的设计。
如果转型是必需要,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需将转型放进他们自己的代码。
宁可使用C++style(新式)转型,不要使用旧式转型,前者很容易被辨识,而且也比较角着分门别类的职掌。
条款28:避免返回handles指向对象的内部成分
避免返回handles(包括reference、指针、迭代器)指向对象内部。遵守这个条款可以增加封装性,帮助const成员函数的行为像个const,
并将发生“虚吊号码牌”的可能性降至最低。
条款29:为“异常安全”而努力是值得的
异常安全函数即使发生异常也不会泄漏资源或允许任何数据结构败坏。这样的函数区分为三种可能的保证:基本型、强烈型、不抛异常型。
强烈保证往往能够以copy-and-swap实现出来,但“强烈保证”并非对所有函数都可以实现或具备现实意义。
函数提供的“异常安全保证”通常最高只等于其所调用之各个函数的“异常安全保证”中最弱者。
条款30:透彻了解inlining的里里外外
关于inline:
1. inline函数的调用,是对函数本体的调用,是函数的展开,使用不当会造成代码膨胀。
2. 大多数C++程序的inline函数都放在头文件,inlining发生在编译期。
3. inline函数只代表“函数本体”,并没有“函数实质”,是没有函数地址的。
4. inline只是一个申请,编译器可以忽略;
5. 包含循环、递归的函数不要声明为inline,virtual函数只在运行时才知道,inline 函数在执行前就替换,因此virtual函数也不要声明为inline。
值得注意的是:
1. 构造函数与析构函数往往不适合inline。因为这两个函数都包含了很多隐式的调用,而这些调用付出的代价是值得考虑的。可能会有代码膨胀的情况。
2. inline函数无法随着程序库升级而升级。因为大多数都发生在编译期,升级意味着重新编译。
3. 大部分调试器是不能在inline函数设断点的。因为inline函数没有地址。
请记住
1. 大多数inlining限制在小型、被频繁调用的函数身上。这可使日后的调试过程和二进制升级更容易,也可以使潜在的代码膨胀问题最小化,使程序的速度提升机会最大化。
2. 另外,对function templates的inline也要慎重,保证其所有实现的函数都应该inlined后再加inline。
条款31:将文件间的编译依存关系降至最低
支持“编译依存性最小化”的一般构想是:相依于声明式,不要相信于定义式。基于此构想的两个手段是Handles classes和Interface classes。
程序库头文件应该以“完全且仅有声明式”的形式存在,这种做法不论是否涉及templates都适用。