C++箴言:理解inline化的介入和排除(二)

2014-11-24 13:13:29 · 作者: · 浏览: 12
生关于析构的反向过程。如果在一个对象构造期间有一个异常被抛出,这个对象已经完成构造的任何部分都被自动销毁。所有这些情节,C++ 只说什么必须发生,但没有说如何发生。那是编译器的实现者的事,但显然这些事情不会自己发生。在你的程序中必须有一些代码使这些事发生,而这些代码――由编译器写出的代码和在编译期间插入你的程序的代码――必须位于某处。有时它们最终就位于构造函数和析构函数中,所以我们可以设想实现为上面那个声称为空的 Derived 的构造函数生成的代码就相当于下面这样:

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
}
}

   这些代码并不代表真正的编译器所生成的,因为真正的编译器会用更复杂的方法处理异常。尽管如此,它还是准确地反映了 Derived 的“空”构造函数必须提供的行为。不论一个编译器的异常多么复杂,Derived 的构造函数至少必须调用它的数据成员和基类的构造函数,而这些调用(它们自己也可能是 inline 的)会影响它对于 inline 化的吸引力。

   同样的原因也适用于 Base 的构造函数,所以如果它是 inline 的,插入它的全部代码也要插入 Derived 的构造函数(通过 Derived 的构造函数对 Base 的构造函数的调用)。而且如果 string 的构造函数碰巧也是 inline 的,Derived 的构造函数中将增加五个那个函数代码的拷贝,分别对应于 Derived 对象中的五个 strings(两个继承的加上三个它自己声明的)。也许在现在,为什么说是否 inline 化 Derived 的构造函数不是一个不经大脑的决定就很清楚了。类似的考虑也适用于