首先调用哪个构造函数

时间:2013-04-16 09:45:20

标签: c++ exception constructor

以下代码来自“异常处理”。作者试图告诉我们,通过使一切成为对象,我们可以防止资源泄漏。 我的问题: 为什么'cat'和'dog'的构造函数比'useresources'的构造函数更早被调用?

//: C07:Wrapped.cpp
// Safe, atomic pointers
#include <fstream>
#include <cstdlib>
using namespace std;
ofstream out("wrapped.out");
// Simplified. Yours may have other arguments.
template<class T, int sz = 1> class PWrap {
    T* ptr;
public:
    class RangeError {}; // Exception class
    PWrap() {
        ptr = new T[sz];
        out << "PWrap constructor" << endl;
    }
    ~PWrap() {
        delete []ptr;
        out << "PWrap destructor" << endl;
    }
    T& operator[](int i) throw(RangeError) {
        if(i >= 0 && i < sz) return ptr[i];
        throw RangeError();
    }
};
class Cat {
public:
    Cat() { out << "Cat()" << endl; }
    ~Cat() { out << "~Cat()" << endl; }
    void g() {}
};
class Dog {
public:
    void* operator new[](size_t sz) {
        out << "allocating an Dog" << endl;
        throw int(47);
    }
    void operator delete[](void* p) {
        out << "deallocating an Dog" << endl;
        ::delete p;
    }
};
class UseResources {
    PWrap<Cat, 3> Bonk;
    PWrap<Dog> Og;
public:
    UseResources() : Bonk(), Og() {
        out << "UseResources()" << endl;
    }
    ~UseResources() {
        out << "~UseResources()" << endl;
    }
    void f() { Bonk[1].g(); }
};
int main() {
    try {
        UseResources ur;
    } catch(int) {
        out << "inside handler" << endl;
    } catch(...) {
    out << "inside catch(...)" << endl;
    }
} ///:~

3 个答案:

答案 0 :(得分:3)

  

为什么'cat'和'dog'的构造函数比'useresources'的构造函数更早被调用?

在输入UseResources的构造函数的 body 之前调用它们。

UseResources有两个数据成员,它们是PWrap<>类模板的实例。 PWrap<T>的构造函数实例化了许多T类型的对象:

ptr = new T[sz];

因此,在您的情况下,TCatDog的构造函数会产生相应数量的调用。

由于PWrap个对象是UseResources的数据成员,因此在输入UseResources构造函数的主体之前执行它们的构造函数。这就是对象构造在C ++中的工作原理。

这背后的基本原理是确保在输入构造函数的主体时,所有子对象的构造函数(包括基础子对象和成员子对象 - 例如Bonk并且Og)已经完成。

这样,构造函数可以依赖于使用有效的子对象,这些子对象在执行时已经建立了类不变量。

这就是C ++ 11标准的第12.6.2 / 10段描述该过程的方式:

  

在非委托构造函数中,初始化按以下顺序进行:

     

- 首先,只有最派生类(1.8)的构造函数,才初始化虚拟基类   它们出现在基类的有向无环图的深度优先从左到右遍历中的顺序,   其中“从左到右”是派生类base-specifier-list中基类出现的顺序。

     

- 然后,直接基类按声明顺序初始化,因为它们出现在base-specifier-list中   (无论mem-initializers的顺序如何)。

     

- 然后,按照在类定义中声明的顺序初始化非静态数据成员   (再次无论mem-initializers的顺序如何)

     

- 最后,执行构造函数体的复合语句

     

[注意:声明命令的目的是确保基础和成员子对象被销毁   初始化的逆序。 - 结束记录]

答案 1 :(得分:1)

构造函数调用的顺序是:

  1. 基础课。
  2. 会员,按顺序显示在标题
  3. 类构造函数。
  4. 类UseResources在构造函数的主体被调用之前被“构造”,关于它具有大小,并且成员变量具有适当的地址。但是它们尚未完全构建。

    构造函数的主体可以假设其所有成员都已完全构造(调用其构造函数),因此必须按此顺序调用它们。

    因此,Bonk和Og在UseResources之前按顺序调用它们的构造函数。

答案 2 :(得分:0)

在代码段中:

   UseResources() : Bonk(), Og() {
      out << "UseResources()" << endl;
   }

实际上,您在构建Bonk和Og成员之前调用了UseResources构造函数,但稍后只是将日志分流了