【问题标题】:Which constructor is called first首先调用哪个构造函数
【发布时间】:2013-04-16 09:45:20
【问题描述】:

以下代码来自 .作者试图告诉我们,通过将一切都变成对象,我们可以防止资源泄漏。 我的问题: 为什么 '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;
    }
} ///:~

【问题讨论】:

  • 他们不是。但它们在out &lt;&lt; "UseResources()" &lt;&lt; endl; 之前被调用。当您进入构造函数的主体时,所有成员对象都将调用它们的构造函数。

标签: c++ exception constructor


【解决方案1】:

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

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

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

ptr = new T[sz];

从而导致对TCatDog,在您的情况下)的构造函数的相应调用次数。

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

这样做的基本原理是确保在进入构造函数的主体时,所有子对象(包括基础子对象和 member 子对象 - 例如 Bonk 和 @ 987654334@) 已完成。

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

C++11 标准的第 12.6.2/10 段是这样描述这个过程的:

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

——首先,并且仅对于最派生类(1.8)的构造函数,虚拟基类在 它们出现在基类的有向无环图的深度优先从左到右遍历中的顺序, 其中“从左到右”是派生类基说明符列表中基类的出现顺序。

——然后​​,直接基类按照它们出现在基说明符列表中的声明顺序进行初始化 (不管 mem-initializers 的顺序如何)。

然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化 (同样不管内存初始化器的顺序)

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

[ 注意:声明顺序是为了确保基子对象和成员子对象在 初始化的相反顺序。 —尾注 ]

【讨论】:

    【解决方案2】:

    构造函数调用顺序为:

    1. 基类。
    2. 成员,按它们出现在标题中的顺序
    3. 类构造函数。

    类 UseResources 是在调用构造函数的主体之前“构造”的,因为它具有大小,并且成员变量具有正确的地址。然而,它们还没有完全建成。

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

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

    【讨论】:

      【解决方案3】:

      在代码段中:

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

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

      【讨论】:

        猜你喜欢
        • 2010-09-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-02
        • 2018-12-02
        • 2017-12-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多