C ++构造函数&析构函数顺序

时间:2012-03-27 12:00:23

标签: c++ constructor destructor

我正在尝试关于基类和成员构造和破坏的代码,我对构造函数和析构函数的某些顺序感到困惑, 此代码的输出是:

Base1 constructor
Member1 constructor
Member2 constructor
Derived1 constructor
Member3 constructor
Member4 constructor
Derived2 constructor
Derived2 destructor
Member4 destructor
Member3 destructor
Derived1 destructor
Member2 destructor
Member1 destructor
Base1 destructor

见前四行, 但是我的命令应该是

Base1 constructor
Derived1 constructor
Member1 constructor
Member2 constructor

任何人都可以给我一些解释吗?

#include "stdafx.h"
#include <fstream>
using namespace std;
ofstream out("order.out");

#define CLASS(ID) class ID { \
public: \
  ID(int) { out << #ID " constructor\n"; } \
  ~ID() { out << #ID " destructor\n"; } \
};

CLASS(Base1);
CLASS(Member1);
CLASS(Member2);
CLASS(Member3);
CLASS(Member4);

class Derived1 : public Base1 {
  Member1 m1;
  Member2 m2;
public:
  Derived1(int) : m2(1), m1(2), Base1(3) {
    out << "Derived1 constructor\n";
  }
  ~Derived1() {
    out << "Derived1 destructor\n";
  }
};

class Derived2 : public Derived1 {
  Member3 m3;
  Member4 m4;
public:
  Derived2() : m3(1), Derived1(2), m4(3) {
    out << "Derived2 constructor\n";
  }
  ~Derived2() {
    out << "Derived2 destructor\n";
  }
};

int main() {
  Derived2 d2;
} ///:~

5 个答案:

答案 0 :(得分:9)

构造函数在层次结构中向上调用:

- base class member objects
- base class constructor body
- derived class member objects
- derived class constructor body

输出正确。

让我们简化您的代码:

struct BaseMember
{
   BaseMember() { cout << "base member" <<endl; }
};
struct Base
{
   BaseMember b;
   Base() { cout << "base" << endl; }
};
struct DerivedMember
{
   DerivedMember() { cout << "derived member" << endl; }
};
struct Derived : public Base
{
   DerivedMember d;
   Derived() { cout << "derived" << endl; }
};

Derived d;

创建d后,它将首先创建Base部分。在进入构造函数体之前,将初始化所有成员对象。所以BaseMember是初始化的第一个对象。

接下来,输入Base的构造函数。

之前,Derived的构造函数进入,Derived的成员对象被初始化,因此创建DerivedMember,调用下一个Derived构造函数。

这是因为当您输入派生类的构造函数体时,必须完全初始化基类和成员对象。

编辑正如Matthieu所指出的,成员对象初始化的顺序由它们在类定义中出现的顺序指定,而不是它们在初始化列表中出现的顺序

答案 1 :(得分:2)

该类的构造函数比其字段的构造函数执行得晚。基类的构造函数及其所有成员的执行时间早于派生类及其成员的构造函数。

答案 2 :(得分:1)

初始化列表中的初始化在Derived1构造函数的主体之前发生,因此您首先看到m1和m2的输出。

作为一个更完整的答案,会发生这样的事情:构造第一个基础子对象,然后按照它们在类中的声明顺序构造成员(而不是它们在初始化列表中的顺序),然​​后执行构造函数体。破坏以相反的顺序发生。

在这种情况下,当您构造Derived2时,它首先构造Derived1子对象。这又涉及首先构建Base子对象,因此它首先执行此操作。然后构造Derived1的成员,执行Derived1构造函数的主体,构造Derived2的成员,最后执行Derived2构造函数的主体。因此观察到的输出。

答案 3 :(得分:1)

这只是因为m1m2Derived1构造函数的初始化列表中初始化。初始化列表中的所有内容都是在输入构造函数体之前构造的。

答案 4 :(得分:1)

考虑以下程序,这将使您的想法清晰!

#include<iostream.h>
class A
{
public:
  A()
    {
    cout<<"\nI am the base class constructor!";
    }
  ~A()
    {
    cout<<"\nI am the base class destructor!";
    }
};

class B : public A
{
public:
  B()
    {
    cout<<"\nI am the derived class constructor!";
    }
  ~B()
    {
    cout<<"\nI am the derived class destructor!";
    }
};

int main()
  {
  B obj;
  return 0;
  }

以上程序的输出如下。

我是基类构造函数!

我是派生类构造函数!

我是派生类析构函数!

我是基类析构函数!

我认为这解释并阐明了关于构造函数和析构函数调用顺序的查询。