Home > Back-end >  Order of destructing objects (c )
Order of destructing objects (c )

Time:01-28

I want to understand the order that objects are destructed in the end of a scope. I understand the right order when dealing with inheritance, but I dont understand it when dealing with containment.

Consider the following program:

using namespace std;

class A{
    public:
        A(){cout<<"A ctor"<<endl;}
        ~A() {cout<<"A dtor"<<endl;}
};
class B{
    public: B(){cout<<"B ctor"<<endl;}
    public: ~B() {cout<<"B dtor"<<endl; }

    private: A a;
};
class C{
    public: C() {cout<<"C ctor"<<endl;}
    public: ~C() {cout<<"C dtor"<<endl;}
    private: B b;
};

class D: public C{
    public: D(){cout<<"D ctor"<<endl;}
    public: ~D() {cout<<"D dtor"<<endl;}
};

int main()
{
    D d;
    return 0;
}

And the output:

A ctor
B ctor
C ctor
D ctor
D dtor
C dtor
B dtor
A dtor

While the construction order is very clear, I do not understand the destruction order. Since D is the "top layer", I would expect it to be destructed first - which in accordance with the output. Then, I would expect the lower layer, C, to be destructed. But C has a field B, and in order for C to be destructed, I think it makes more sense that its fields would be destructed first, which is not in accordance with the output.

I'd really appreciate an explanation. Thanks in advance.

CodePudding user response:

But C has a field B, and in order for C to be destructed, I think it makes more sense that its fields would be destructed first, which is not in accordance with the output.

Why do you think that would make more sense? In the destructor of C you might want to do some cleanup of the members, or based on the member states. Like manual memory management, releasing of other resources, more efficient and none recursive (to prevent stack overflows) destructing of a tree structure, ... .

So those members have to be available to the destructor, otherwise, you couldn't do much in the destructor of C except logging that it was called.

CodePudding user response:

I guess that D contains C - which is missing from your code.

When destructing, the D destructor is called when D goes out of scope. Then your print your output. Then as execution reaches the end of the destructor the C destructor is called as it is going out of scope. Then after the C destructor completes, the D constructor is considered complete. So, in a way, your understanding is partially correct. The D constructor contains the C destructor, but your output happens before the C destructor is called.

CodePudding user response:

The destructor C gets the control. So this message within the destructor body is outputted

public: ~C() {cout<<"C dtor"<<endl;}

after the body of the destructor executed data members of the class C are destroyed.

As the class C has one data member

private: B b;

when the destructor of this data member is called and the body of the destructor B is executed. And so on.

That is before destructing an object its destructor gets the control and its body is executed.

Opposite to destructors bodies of constructors get the control after objects already created.

Bodies of destructors get the control for objects that still exist to allow you to output a message like :)

C dtor

CodePudding user response:

Consider this simple mental model:

Within any object's destructor, you may assume that the entire object still exists.

The destruction order in your code is the only order that respects this model.

  • Your ~D() may assume that its base class C may still be accessed during destruction.
  • Your ~C() may still access its member variable B b;.
  • Your ~B() may still access its member variable A a;.

If destruction were performed in any other order, writing a nontrivial destructor would be very difficult, as you would not know what base classes or member variables you can still access.

  •  Tags:  
  • Related