What order are destructors called and member variables destroyed in C++ using inhertitance?

22,476

Solution 1

constructor: first base, then derived

destruction:

  • ~derived
  • ~member derived
  • ~base
  • ~member base

code:

class member {
    string s;

public:
    member(string s) {
        this-> s = s;
    }

    ~member() {
        cout << "~member " << s << endl;
    }
};

class base {
    member m;
public:
    base() : m("base"){
    }

    ~base() {
        cout << "~base" << endl;
    }
};

class derived : base{
     member m2;
public:

    derived() :m2("derived") {    }

    ~derived() {
        cout << "~derived" << endl;
    }
};

int main(int argc, const char * argv[]) {
    derived s;

    return 0;
}

References & virtual destructor

When you plan to dynamically allocate (i.e. when you use the keywords new & delete) a derived object, then always have a virtual or a protected destructor on your base. Dynamically deleting the object on the base class reference would otherwise lead to memory leaks in the example below:

class base {
    member m;
public:
    base() : m("base"){
    }

    /* correct behaviour is when you add **virtual** in front of the signature */
    ~base() {
        cout << "~base" << endl;
    }
};

class derived : public base{
     member m2;
    char* longArray;
public:

    derived() :m2("derived") {
        longArray = new char[1000];
    }


    ~derived() {
        delete[] longArray; // never called
        cout << "~derived" << endl;
    }
};

int main(int argc, const char * argv[]) {
    base *s = new derived; // mind the downcast to **base**

    delete s; /* only the non-virtual destructor on the base and its members is called. 
               No destructor on derived or its members is called.
               What happens to the memory allocated by derived?
               **longArray** is leaked forever. 
               Even without **longArray**, it most probably **leaks** memory, as C++ doesn't define its behaviour 
               */
    return 0;
}

Output:

  • ~base
  • ~member base

Only base data is cleaned up, and longArray leaks.

Solution 2

Here's what the standard says... (C++11, 12.4/8)

After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X’s direct non-variant non-static data members, the destructors for X’s direct base classes and, if X is the type of the most derived class (12.6.2), its destructor calls the destructors for X’s virtual base classes. All destructors are called as if they were referenced with a qualified name, that is, ignoring any possible virtual overriding destructors in more derived classes. Bases and members are destroyed in the reverse order of the completion of their constructor (see 12.6.2). A return statement (6.6.3) in a destructor might not directly return to the caller; before transferring control to the caller, the destructors for the members and bases are called. Destructors for elements of an array are called in reverse order of their construction (see 12.6).

Note that this order is indeed the reverse of the order given in 12.6.2/10 in C++11. You can't tell what the order of destruction of virtual bases is from looking at 12.4/8 alone, but you can infer it from 12.6.2/10, which specifies that initialization of virtual bases occurs in depth-first search left-to-right order. (Thus, destruction of virtual bases occurs in the reverse of that order.)

Anyway, you have your answer. Non-static members are destroyed first, then base classes. But a base class's members will be destroyed before the next base class's destructor starts. It really is exactly like depth-first search.

Share:
22,476
mczarnek
Author by

mczarnek

Updated on July 05, 2022

Comments