现在的位置: 首页 > 综合 > 正文

Effective C++学习笔记之第五章(3)

2017年11月21日 ⁄ 综合 ⁄ 共 2172字 ⁄ 字号 评论关闭

chapter 5 实现

item30 透彻了解inline的里里外外
1)当需要定义一个小型的的函数时,inline比宏要好,不容易出错,也不会有函数调用所需要的开销。编译器会直接将inline函数出现的地方直接用函数体替换,明显这样会引起代码膨胀、指令击中率低以及由此引发的一些效率问题。所有这个函数最好是被频繁调用,这样才会有我们预期的最小化代码膨胀问题和最大化程序的速度。
2)一般来说编译器会拒绝将太复杂(包含循环或递归)的函数以及虚函数inline,因为虚函数的具体调用的函数是在runtime时确定的。当然,如果编译器没有成功inline一个函数,会发出一个警告信息。另外inline包括隐式的inline和显式的inline。
3)如果程序要取某个inline函数的地址,编译器通常必须为这个函数生成一个outline函数本体。因为编译器没有能力让一个指针指向并不存在的函数,并且编译器不会对"通过函数指针进行调用"的函数实施inline。所有inline函数的调用是否inline取决于它的调用方式。

inline void f() {...} // assume compilers are willing to inline calls to f
void (*pf)() = f;    // pf points to f
...
f();    // this call will be inlined, because it's a "normal" call
pf();  // this call probably won't be, because it's through a function pointer

4)不要把构造函数和析构函数写成inline。不论编译器在其内所做的异常处理多么精致复杂,派生类的构造函数会陆续调用其基类和其成员变量的构造函数,而这些构造函数可能本身就是inline的,这样就会影响编译器是否对派生类构造函数inline。类似的准则也适用于基类的构造函数以及类的析构函数。

class Base {
public:
 ...
private:
   std::string bm1, bm2;               // base members 1 and 2
};
class Derived: public Base {
public:
  Derived() {}                         // Derived's ctor is empty — or is it?
  ...
private:
  std::string dm1, dm2, dm3;           // derived members 1–3
};
//看起来派生类的构造函数里面一句代码都没有,非常适合inline。
//但是实际上编译器为我们生成的构造函数的函数体可以像下面一样,你觉得这样还适合inline?
Derived::Derived()                       // conceptual implementation of
{                                        // "empty" Derived ctor
 Base::Base();                           // initialize Base part
 try { dm1.std::string::string(); }      // try to construct dm1
 catch (...) {                           // if it throws,
   Base::~Base();                        // destroy base class part and
   throw;                                // propagate the exception
 }
 try { dm2.std::string::string(); }      // try to construct dm2
 catch(...) {                            // if it throws,
   dm1.std::string::~string();           // destroy dm1,
   Base::~Base();                        // destroy base class part, and
   throw;                                // propagate the exception
 }
 try { dm3.std::string::string(); }      // construct dm3
 catch(...) {                            // if it throws,
   dm2.std::string::~string();           // destroy dm2,
   dm1.std::string::~string();           // destroy dm1,
   Base::~Base();                        // destroy base class part, and
   throw;                                // propagate the exception
 }
}

5)inline函数无法随着程序库的升级而升级。如果程序库内的一个inline函数f,客户用了这个函数f。一旦程序库中的f有任何改变,所有用到f的程序都必须重新编译。但如果是non-inline函数,如果有任何修改,只需要重新连接就好了,这比重新编译的负担少得多。如果程序库采用动态链接,那么被改变的函数甚至可以不知不觉的就被应用程序吸纳了。
6)inline函数的调试很难,因为没办法在一个不存在的函数里面去设置一个断点。许多建置环境只能在调试版的程序中禁止发生inline。
7)80-20经验法则:平均而言,一个程序往往将80%的执行时间花费在20%的代码上面。我们的目标就是找出这可以有效的增进程序整体效率的20%代码,然后将它inline或竭尽所能地将它瘦身。除非选对目标,否则一切都是徒劳。
8)不要只因为function templates出现在头文件,就将它们声明为inline。因为你写的function template,没理由说它所实现的具体的每个函数都是inline的吧~

抱歉!评论已关闭.