首先调用哪个构造函数

Which constructor is called first

本文关键字:构造函数 调用      更新时间:2023-10-16

下面的代码来自。作者试图告诉我们,通过将所有东西都变成对象,我们可以防止资源泄漏。我的问题:为什么"cat"answers"dog"的构造函数比"userresources"的构造函数更早被调用?

//: 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;
    }
} ///:~

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

UseResources的构造函数输入之前被调用。

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

ptr = new T[sz];

从而导致对T(在您的示例中为CatDog)的构造函数的相应次数的调用。

由于PWrap对象是UseResources的数据成员,它们的构造函数在进入UseResources构造函数体之前执行。这就是c++中对象构造的工作方式。

这样做的基本原理是确保在进入构造函数体时,所有子对象(包括基子对象和成员子对象——如BonkOg)的构造函数都已完成。

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

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

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

-首先,并且仅对最派生类的构造函数(1.8)初始化虚基类它们在基类的有向无环图从左到右的深度优先遍历中出现的顺序,其中"从左到右"是派生类基-指定符列表中基类的出现顺序。

-然后,直接基类按照它们出现在base-specifier-list中的声明顺序初始化(不管初始化式的顺序如何)

- 然后,按照在类定义中声明的顺序初始化非静态数据成员.

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

[注意:强制声明顺序以确保基和成员子对象在初始化的相反顺序。- 结束说明]

调用构造函数的顺序为:

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

类userresources在构造函数体被调用之前被"构造",因为它有一个大小,并且成员变量有适当的地址。然而,它们还没有完全构建。

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

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

代码段:

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

实际上你是在构造Bonk和Og成员之前调用userresources构造函数,但只是稍后输出你的日志