五:实现
条款26:尽可能延后变量定义式的出现时间
"尽可能延后"的真正意义:不只应该延后变量的定义,直到非得使用该变量的前一刻为止,甚至应该尝试延后这份定义直到能够给它初值实参为止。如果这样,不仅能够避免构造(和析构)非必要对象,还可以避免无意义的default构造函数。更深一层说,以“具明显意义之初值”将变量初始化,还可以附带说明变量的目的。
//伪代码,说明“能够避免构造(和析构)非必要对象“
void A(){
string str;
if(....){
throw(...);
}
...
}
//在这个函数中,str被过早定义,如果函数抛出异常,就会付出str的构造和析构代价
请记住:尽可能延后变量定义式的出现。这样做可增加程序的清晰度并改善程序效率。
条款27:尽量少做转型动作
C++提供四种新式转型: const_cast();dynamic_cast();reinterpret_cast();static_cast();
请记住:1.如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_casts。如果有个设计需要转型动作,试着发展无需转型的替代设计。
2.如果转型是必要的,试着将他隐藏于某个函数背后。客户随后可以调用该函数,而不需将转型放进他们自己的代码中。
3.宁可使用C+±style(新式)转型,不要使用旧式转型。前者很容易辨识出来,而且也比较有着分门别类的执掌。
条款28:避免返回handles指向对象内部成分
//简单的例子
class A(){
public:
int & ret() const{
return a;
}
private:
int a;
};
/*
两个教训:①成员变量的封装性最多只等于“返回其reference/point”的函数的访问级别。本例中虽然a被声明为private,但是他们实际上确实public,因为public函数ret传出了他们的reference。
②如果const成员函数传出一个reference,后者所指数据与对象自身有关联,而它又被存储于对象之外,那么这个函数的调用者可以修改那笔数据。这正是bitwise constness的一个附带结果,见条款3.
还可能导致dangling handles(空悬的号码牌):这种handles所指东西(的所属对象)不复存在。这种“不复存在的对象”最常见的来源就是函数返回值。不要返回指向函数内局部变量的指针和引用。
请记住:避免返回handles(包括references、指针、迭代器)指向对象内部。遵守这个条款可增加封装性,帮助const成员函数的行为像个const,并将发生“虚吊号码牌”的可能性降至最低。
条款29:为“异常安全”而努力是值得的
异常安全函数提供以下三个保证之一:
①基本承诺:如果异常被抛出,程序内的任何事物仍然保持在有效状态下。
②强烈保证:如果异常被抛出,程序状态不改变。
③不抛制保证:承诺绝不抛出异常,因为他们总是能够完成他们原先承诺的功能。
copy-and-swap策略:为你打算修改的对象(原件)做出一份副本,然后在那副本身上做一切必要修改。若有任何修改动作抛出异常,原对象仍保持非修改状态。待所有改变都成功后,再将修改过的那个副本和原对象在一个不抛出异常的操作中置换(swap)。
如何让他具备异常安全性:首先是“以对象管理资源”,那可阻止资源泄露。然后是挑选三个“异常安全保证”中的某一个实施于你所写的每一个函数身上。
请记住:1.异常安全函数即使发生异常也不会泄露资源或允许任何数据结构败坏。这样的函数区分三种可能的保证:基本型、强烈型。不抛异常型。
2.“强烈保证”往往能够以copy-and-swap实现出来,但“强烈保证”并非对所有函数都可实现或具备显示意义。
3.函数提供的“异常安全保证”通常最高只等于其所调用之各个函数的“异常安全保证”中的最弱者。
条款30:透彻了解inlining的里里外外
inlinling在大多数C++程序中是编译期行为
inline函数和Template通常被置于头文件内。
大部分编译器拒绝将太过复杂(带有循环或递归)的函数inlinling。而所有对virtual函数的调用也会使inlinling落空,因为virtual意味着等待,直到运行期才确定调用哪个函数,而inline意味执行前,先将调用动作替换为被调用函数的本体。
编译器通常不对“通过函数指针而进行的调用”实施inlinling。
请记住:1.将大多数inlining限制在小型、被频繁调用的函数身上。这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化,使程序的速度提升机会最大化。
2.不要只因为function template出现在头文件,就将他们声明为inline。
条款31:将文件间的编译依存关系降至最低
Handle classes 和 Interface classes解除了接口和实现之间的耦合关系,从而降低文件间的编译依存性。
如果使用 object reference 或 object pointers可以完成任务,就不要使用objects
如果能够,尽量以class声明式替换class定义式。
为声明式和定义式提供不同的头文件。
请记住:1.支持“编译依存性最小化”的一般构想是:相依于声明式,不要相依于定义式。基于此构想的两个手段是Handles classes和Interface classes。
2.程序库头文件应该以“完全且仅有声明式”的形式存在。这种做法不论是否涉及template都适用。