2
using namespace std;

class C
{
    int a;
    public:
        C(int aa=0) {a=aa;}
        ~C() {cout << "Destructor C!" << a << endl;}
};

class D: public C
{
    int b;
    public:
        D(int aa=0, int bb=0): C(aa) {b=bb;}
        ~D() {cout << "Destructor D!" << b << endl;}
};

void test()
{
    D x(5);
    D y(6,7);
}

int main()
{
    test();
}

上面是代码,下面是运行结果:

Destructor D!7
Destructor C!6
Destructor D!0
Destructor C!5

我不明白为什么“析构函数 C!” 将被调用。以及相关的析构函数的调用顺序。我觉得这似乎是堆栈推送/弹出。

进一步: 为什么前面调用了“ D x(5); ”,而后面给出了相应的结果呢?

4

8 回答 8

3

派生类的构造函数和析构函数调用顺序如下:

Base  Class  Constructor
Derived  Class  Constructor
Derived  Class  Destructor
Base  Class  Destructor

由于派生类建立在基类之上:

  • 基类必须在派生类之前构造。
  • 派生类必须在基类之前销毁。
于 2012-12-21T11:09:00.620 回答
3

析构函数调用序列总是从派生到基,就像弹出堆栈一样。这允许派生类清理基类分配的资源。在这种情况下,编译器知道如何构造这个序列,因为它知道对象的确切类型x并且是y静态的。

然而,在某些情况下,这个顺序会被打破。考虑对您的代码进行以下修改:

void test()
{
    C *x = new D(5);
    D *y = new D(6,7);
    delete x;
    delete y;
}

它产生以下输出:

Destructor C!5
Destructor D!7
Destructor C!6

运行它不会产生~Dfor x; 对于y,两个析构函数都被调用。

这是因为您没有virtual在基类中声明析构函数。当析构函数不是虚拟的时,编译器不知道它必须在对象被指向基类的指针引用的情况下调用派生类的析构函数。这就是为什么你应该总是在必须继承和动态分配资源的类中创建一个虚拟的析构函数。

于 2012-12-21T11:16:14.170 回答
2
 I feel that it seems like the stack push/pop.

D类派生自C,调用D构造函数时,先调用C构造函数,反之调用析构函数。

Further: Why it calls "D x(5);" earlier but the corresponding result is given later?

当控制流离开其定义范围时,自动对象(通常称为“局部变量”)将按照其定义的相反顺序被破坏。

于 2012-12-21T11:14:42.767 回答
1

清理对象时,首先调用派生类的析构函数,然后调用基类的析构函数。

于 2012-12-21T10:59:20.350 回答
1

创建D实例时,C将调用 的构造函数,因为D继承自它。
当你D再次销毁时,它会调用Ds 析构函数,然后调用析C构函数。

于 2012-12-21T10:59:31.017 回答
1

当你继承时,你“扩展”了你继承的对象。因此,如果您要构建D,则需要C. 当您需要销毁时D,您将使用销毁C,您构建它以便能够扩展它。

于 2012-12-21T11:02:57.157 回答
0

进一步:为什么它叫“D x(5);” 较早但相应的结果在后面给出?

这些对象在堆栈上分配(而D * d1 = new D();堆上分配)。

提供输出的不是调用D x(5),而是当实例超出范围时对析构函数的调用~D,在这种情况下,是在 exiting 时main()

因为它是堆栈内存,所以解除分配的顺序与分配相反;

于 2012-12-21T11:32:44.237 回答
0

调用析构函数C是因为销毁派生类的对象包括销毁其基类子对象。根据 [class.dtor] 第 8 段:

在执行析构函数的主体之后……类 X 的析构函数调用……X 的直接基类的析构函数……

当控制流退出一个范围时(例如你的函数test()结束),本地对象以 LIFO 方式被销毁:首先创建的对象最后被销毁。根据 [stmt.jump] 第 2 段:

从范围退出时(无论如何完成),已在该范围内构建的具有自动存储持续时间 (3.7.3) 的对象将按其构建的相反顺序销毁。

于 2012-12-21T11:13:42.453 回答