事实上,构造函数和析构函数对于 inline 化来说经常是个比您在不经意的检查中所能显示出来的更加糟糕的候选者。例如,考虑下面这个类 Derived 的构造函数:
| 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 化的极好的候选者,因为他不包含代码。但是视觉会被欺骗。
C 为对象被创建和被销毁时所发生的事情做出了各种确保。例如,当您使用 new 时,您的动态的被创建对象会被他们的构造函数自动初始化,而当您使用 delete。则相应的析构函数会被调用。当您创建一个对象时,这个对象的每一个基类和每一个数据成员都会自动构造,而当一个对象被销毁时,则发生关于析构的反向过程。假如在一个对象构造期间有一个异常被抛出,这个对象已完成构造的任何部分都被自动销毁。任何这些情节,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 } } |




