【问题标题】:Explain order of constructor/destructor calls in this C++ code解释此 C++ 代码中构造函数/析构函数调用的顺序
【发布时间】:2023-03-31 16:31:01
【问题描述】:

我有这个 C++ 代码:

class BaseClass {
    int id;
public:
    BaseClass() { printf("BaseClass()\n"); }
    virtual ~BaseClass() { printf("~BaseClass()\n"); }
};

class Class1 : public BaseClass
{
    int id;
public:
    Class1() { printf("Class1()\n"); }
    ~Class1() { printf("~Class1()\n"); }
};

class Class2 : public Class1
{
    BaseClass id;
public:
    Class2() { printf("Class2()\n"); }
    ~Class2() { printf("~Class2()\n"); }
};

class Class3 : virtual public BaseClass
{
    int id;
public:
    Class3() { printf("Class3()\n"); }
    ~Class3() { printf("~Class3()\n"); }
};

class Class4 : public Class3, virtual public Class1
{
    Class3 id;
public:
    Class4() { printf("Class4()\n"); }
    ~Class4() { printf("~Class4()\n"); }
};

int main(int argc, char* argv[])
{
    BaseClass *p = new Class2;
    Class2 *p1 = new Class2;
    Class3 *p2 = new Class3;
    delete p;
    delete p1;
    delete p2;
    return 0;
}

这是输出:

BaseClass()
Class1()
BaseClass()
Class2()
BaseClass()
Class1()
BaseClass()
Class2()
BaseClass()
Class3()
~Class2()
~BaseClass()
~Class1()
~BaseClass()
~Class2()
~BaseClass()
~Class1()
~BaseClass()
~Class3()
~BaseClass()

我不明白为什么。我希望输出如下:

BaseClass()
Class1()
Class2()
BaseClass()
Class1()
Class2()
...

等等

在创建p1 时,为什么在Class1() 之后不打印Class2()? 这和虚拟继承有关系吗?

【问题讨论】:

  • 当A类继承B类时,B类的构造需要先构造A类——因为B的构造可能需要使用A
  • @songyuanyao 复制输出时不小心漏掉了第一行,抱歉。

标签: c++ constructor order-of-execution


【解决方案1】:

让我们一步一步地构造第一个对象:

new Class2;

这是您正在构建的第一个对象,我们称之为p。

BaseClass()

p 的BaseClass。

Class1()

p 的Class1,BaseClass 的子类被构造。

BaseClass()

这是正在构造的Class2 的id 成员。

Class2()

现在,最后,Class2、p 本身。

因此,尽管您不这么认为,Class2() 还是在Class1() 之后打印。除了您忘记了Class2 也有一个id 成员,那是一个BaseClass,并且还必须在调用Class2::Class2() 构造函数之前构造它。您认为您看到的是第二个newed 对象的BaseClass 正在构造,但真正构造的是BaseClass 成员对象。

附:这是C++。在C++ 中,我们使用std::cout 而不是printf()。 printf() 真是……上个世纪了。

【讨论】:

  • 当然...我完全忽略了这一点,甚至没有注意到id 不是BaseClass 和Class1 中的整数。这样一个蹩脚的错误:) 顺便说一句,这不是我的代码。 :)
【解决方案2】:

在创建例如 p1 时,为什么在 Class1() 之后不打印 Class2()?

因为Class2有一个类型为BaseClass的非静态成员对象,所以它的ctor会在Class2的ctor之前被调用。

根据initialization order:

初始化顺序

1) 如果构造函数用于最派生类,则虚拟基类 类按照它们出现的顺序进行初始化 基类声明的深度优先从左到右遍历 (从左到右指的是在基本说明符列表中的出现)

2) 然后,直接基类按从左到右的顺序初始化为 它们出现在此类的基本说明符列表中

3) 然后,非静态数据成员按顺序初始化 类定义中的声明。

4) 最后执行构造函数的主体

对于new Class2;,将首先调用直接基类Class1及其基类BaseClass。然后将调用类型为BaseClass 的非静态数据成员id。最后将调用Class2 的ctor 的主体。所以你会得到

BaseClass()
Class1()
BaseClass()
Class2()

【讨论】:

    猜你喜欢
    • 2012-04-10
    • 2013-06-24
    • 2016-03-18
    • 2011-11-24
    • 2010-10-13
    • 1970-01-01
    相关资源
    最近更新 更多