您的位置:首页 > 编程语言 > C语言/C++

《Effective C++ 改善程序与设计的55个具体做法》——第五章笔记

2015-03-22 19:19 288 查看
实现

条款26:尽可能延后变量定义式的出现时间

只要定义了一个变量且该变量类型有构造和析构函数,那么当程序运行到定义式时就会有构造成本,离开作用域时就会有析构成本。即使这个变量并未被使用。

尽可能延后的真正意义:你不只应该延后定义直到非得使用该变量的前一刻为止,甚至应该尝试延后定义直到能够给他初值实参为止。这样不仅能够避免构造和析构非必要对象,还可以避免无意义的默认构造行为。(因为这时不用先调用默认构造函数,再调用赋值操作函数了。)

循环中变量是在循环外定义在循环内赋值还是定义于循环内?定义于循环内相对较好,且其变量作用域更小。一般是这样选择,除非(1)赋值成本比构造+析构成本低(2)代码对效率高度敏感,否则选择定义于循环内。

尽可能延后变量定义式的出现,可以增加程序清晰度并改善程序效率。

条款27:尽量少做转型动作

旧式转型:

C风格:(T)expression

函数风格:T(expression)

C++还提供了四种新式转型:

1、const_cast<T>( expression )

通常用于将对象的常量性去除,它也是唯一有此能力的C++转型操作符。一般针对指针或引用,去除const、volatile、__unaligned等属性。

尽量不要使用(既然定义为const,干嘛还要去掉const,修改其值呢)。

2、dynamic_cast<T>( expression )

主要用来执行安全向下转型,也就是用来决定某对象是否归属继承体系中的某个类型。它是唯一无法由旧式语法执行的动作,也是唯一可能耗费重大运行成本的转型。

动态的运行时转换,并判断是否可以转换,通常类型中含有虚函数。参数必须是有效指针或引用。基类和派生类之间的相互转换。指针时,失败结果为0。引用时失败抛出bad_cast异常。

3、reinterpret_cast<T>( expression )

重新解释类型,意图执行低级转型,实际动作取决于编译器,不可移植。在低级代码外很少见。

4、static_cast<T>( expression )

强迫隐式转换,不会检查是否有效。

新式转型相对较好,因为很容易在代码中辨识。

尽量避免类型转换,特别是在重视效率的代码中避免dynamic_cast,试着用无需转换的设计替代。如果必须转型,试着将其隐藏于某个函数,客户可以调用函数而不需要将转型放进自己代码。

条款28:避免返回handles指向对象的内部成分

可增加封装性,帮助const成员函数的行为像个const,并将发生dangling handles的可能性降至最低。

成员变量的封装性最多只等于返回其reference的函数的访问级别。如果const成员函数传出一个reference,其所指向数据与对象自身有关联而又被存放于对象之外,那么这个函数的调用者可以修改那个数据。

dangling handles指向不复存在的对象。

条款29:为异常安全而努力是值得的

当异常被抛出时,带有异常安全的函数会:1、不泄露任何资源;2、不允许数据败坏。

异常安全函数提供三个保证之一:

1、基本保证:异常抛出,程序内任何事物仍然保持有效状态。没有任何对象或数据结构因此败坏,所有对象处于前后一致的状态。

2、强烈保证:异常抛出,程序状态不改变。如果函数成功就是完全成功,如果函数失败,程序会恢复到调用函数之前的状态。

3、不抛掷保证:承诺不抛出异常,总能够完成承诺的功能。作用于内置类型上的所有操作都提供这个保证。

异常安全代码必须提供上述三者之一,否则就不具备异常安全性。第三种一般很难。

copy and swap策略可以提供强烈保证,原则是:为打算修改的对象做出一个副本,在副本上做一切修改,若有异常原对象保持未改变状态,修改完全成功后,再讲修改过的副本和原对象在不抛出异常的swap中置换。

但要考虑效率,不一定非得提供强烈保证。可以选择基本保证。

函数的异常安全保证最高只等于其所调用的函数中最弱保证。

条款30:透彻了解inlining的里里外外

过度inlining会造成目标代码膨胀。

将函数定义于class的定义式中——隐喻的inline函数,包括成员函数和友元函数。

大多数编译器拒绝太过复杂的函数inlining,所有对virtual函数的调用也会是inlining落空。

一个表面看似inline的函数是否真是inline,取决于建置环境主要是编译器。

有时候虽然编译器有意愿inlining某个函数,还是可能为该函数提供一个函数本体。编译器通常不对通过函数指针而进行的调用实施inlining。

构造函数和析构函数是inlining的糟糕候选人。即使他们是空函数,编译器会在编译期间代为产生很多代码。

将函数声明为inline后,inline函数无法随着程序库的升级而升级,所有客户必须重新编译。

大部分调试器对inline函数束手无策。

将大多数inlining限制在小型被频繁调用的函数身上。

不要只因为函数模板出现在头文件,就将它们声明为inline。

条款31:将文件见的编译依存关系降至最低

用声明的依存性替换定义的依存性。现实中让头文件尽可能自我满足,万一做不到,则让它与其他文件内的声明式而非定义式相依。

如果使用对象引用或指针可以完成任务,就不要使用对象;尽量以类声明式替换类定义式;为声明式和定义式提供不同的头文件。

两种手段:handle classes和inferface classes

程序库头文件应该以完全且仅有声明式的形式存在。这种做法不论是否涉及模板都适用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: