Virtual destructors and deleting objects with multiple inheritance... How does it work?

First, I understand why virtual destructors are needed in terms of single inheritance and deleting an object through a base pointer. This is specifically about multiple inheritance and the reason behind why this works. This question came up in one of my university classes, and no one (including the professor) was sure why this worked:

#include <iostream>

struct A
{
    virtual ~A()
    {
        std::cout << "~A" << std::endl;
    }
    int memberA;
};

struct B
{
    virtual ~B()
    {
        std::cout << "~B" << std::endl;
    }
    int memberB;
};

struct AB : public A, public B
{
    virtual ~AB()
    {
        std::cout << "~AB" << std::endl;
    }
};

int main()
{
    AB* ab1 = new AB();
    AB* ab2 = new AB();

    A* a = ab1;
    B* b = ab2;

    delete a;
    delete b;
}

The output for this is:

~AB ~B ~A ~AB ~B ~A

How does the compiler know how to call A's and B's destructor when deleting a or b? Specifically, how is the memory for AB laid out (particularly it's virtual function table), such that the A and B destructors can be called?

My professor was suggesting that memory would be laid out (something) like this:

    AB
+---------+              +----+
|  A VFT  | - - - - - -> | ~A |
+---------+              +----+
| memberA |
+---------+              +----+
|  B VFT  | - - - - - -> | ~B |
+---------+              +----+
| memberB |
+---------+

// I have no idea where ~AB would go...

We're all curious how these destructors are actually laid out in memory and how calling delete on either a or b results in all the destructors being properly called. It makes sense that deleting a base object works in single inheritance (because there's a single virtual function table to work with), but apparently I'm not understanding things correctly because I can't take my understanding of the single inheritance version and apply it to this multiple inheritance example.

So how does this work?

It works because the standard says that it works.

In practice, the compiler inserts implicit calls to ~A() and ~B() into ~AB(). The mechanism is exactly the same as with single inheritance, except that there are multiple base destructors for the compiler to call.

I think the main source of confusion in your diagram is the multiple separate vtable entries for the virtual destructor. In practice, there will be a single entry that would point to ~A(), ~B() and ~AB() for A, B and AB() respectively.

For example, if I compile your code using gcc and examine the assembly, I see the following code in ~AB():

LEHE0:
        movq    -24(%rbp), %rax
        addq    $16, %rax
        movq    %rax, %rdi
LEHB1:
        call    __ZN1BD2Ev
LEHE1:
        movq    -24(%rbp), %rax
        movq    %rax, %rdi
LEHB2:
        call    __ZN1AD2Ev

This calls ~B() followed by ~A().

The virtual tables of the three classes look as follows:

; A
__ZTV1A:
        .quad   0
        .quad   __ZTI1A
        .quad   __ZN1AD1Ev
        .quad   __ZN1AD0Ev

; B
__ZTV1B:
        .quad   0
        .quad   __ZTI1B
        .quad   __ZN1BD1Ev
        .quad   __ZN1BD0Ev

; AB
__ZTV2AB:
        .quad   0
        .quad   __ZTI2AB
        .quad   __ZN2ABD1Ev
        .quad   __ZN2ABD0Ev
        .quad   -16
        .quad   __ZTI2AB
        .quad   __ZThn16_N2ABD1Ev
        .quad   __ZThn16_N2ABD0Ev

For each class, entry #2 refers to the class's "complete object destructor". For A, this points to ~A() etc.

Multiple inheritance and mix of virtual and non-virtual base class , How to avoid memory leaks with virtual destructors when using inheritance in C++. A double digital pay-off function is a pay-off function. If the object that the pointer is pointing to is deleted, and the destructor is not set to virtual , then the� Deleting a derived class object using a pointer to a base class that has a non-virtual destructor results in undefined behavior. To correct this situation, the base class should be defined with a virtual destructor.

The vtable entry simply points at the destructor for AB. It is just defined that after execution of a destructor, the base class destructors are then called:

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 base classes and [...].

So when the compiler sees delete a; and then sees that the destructor of A is virtual, it looks the destructor up for the dynamic type of a (which is AB) by using the vtable. This finds ~AB and executes it. This results in the calling of ~A and ~B.

It's not the vtable that says "call ~AB, then ~A, then ~B"; it simply says "call ~AB" which involves calling ~A and ~B.

C++ Virtual Destructors: How to Avoid Memory Leaks, What all is inherited from parent class in C++? � Virtual Functions and Deleting a derived class object using a pointer to a base class that has a non-virtual destructor results in undefined behavior. As a guideline, any time you have a virtual function in a class, you should immediately add a virtual� Pure Virtual Destructors in C++. Pure Virtual Destructors are legal in C++. Also, pure virtual Destructors must be defined, which is against the pure virtual behaviour. The only difference between Virtual and Pure Virtual Destructor is, that pure virtual destructor will make its Base class Abstract, hence you cannot create object of that class.

Destructors are called in the order "most derived to most basal", and in reverse order of declaration. So ~AB is called first, then ~B, then ~A, because AB is the most derived class.

All destructors are called before the memory is actually freed. Exactly how the virtual function pointers are stored is an implementation detail, and really something you shouldn't be concerned about. A class with multiple inheritance will most likely contain two pointers to the VTABLES of the classes that it derives from, but as long as the compiler and runtime libraries together "work as we expect", it is entirely up to the compiler + runtime libraries to do what they fancy to solve these sort of issues.

Inheritance — Multiple and Virtual Inheritance, C++ FAQ, in most (all?) current C++ implementations, every object instance of In which case I should NOT use virtual destructors? Either clients should not be able to delete polymorphically using a pointer to Base. The cost of introducing any virtual function to a class (inherited or part of the class definition) is a� In your case of multiple inheritance, it will only work if the base class you're deleting it through has a virtual destructor; it's ok for the other base classes to not have a virtual destructor, but only if you don't try to delete any derived objects via those other base class pointers.

(I know this question is almost two years old but I couldn't resist making a point after I came across it)

Although in the title you use the question word how, you also mention why in the question post. People have given good technical answers for the how but the why seems to have gone unaddressed.

This is specifically about multiple inheritance and the reason behind why this works

This is purely guess work but sounds reasonable to me. The easiest way to look at it is that an object using multiple inheritance is composed of a number of base objects. Selectively destructing a base object will leave a hole in the composite object and that results in needless complexity when handling methods addressed to those sections of the composite object. Imagine how you would do it if you indeed used composition over multiple inheritance. So it is better to walk the object layout and destroy it as a whole.

Virtual Destructor, You can instead rely on virtual inheritance to ensure that no concrete class can Do define a virtual destructor for classes used as base classes that have virtual Both C++ and Python support multiple inheritance, and Boost Python makes it That is, a mock object is instrumented to record all function calls for an object� Destructors indeed are a part of each object's interface, since the object's user is responsible for calling them (i.e. directly with delete or indirectly by letting an object out of scope). Each object has exactly one destructor: its own destructor, which might optionally be a virtual one. It is always its own, and it's not inherited.

When NOT to use virtual destructors?, Undisciplined multiple inheritance Deleting a class without a virtual destructor will only call the destructor of if the pointer type is a base type while the object instance is a derived type. virtual void f() { //has virtual function. However, because many OOP languages do not support multiple inheritance, they often provide a separate interface mechanism. An example is the Java programming language. Behaviour during construction and destruction. Languages differ in their behaviour while the constructor or destructor of an object is running. For this reason, calling virtual

Virtual Destructor, The virtual specifier specifies that a non-static member function is virtual and declares or inherits (through multiple inheritance) another function that overrides vf . This makes it possible to delete dynamically allocated objects of at least one virtual function), and its destructor is not virtual, deleting it is� The easiest way to look at it is that an object using multiple inheritance is composed of a number of base objects. Selectively destructing a base object will leave a hole in the composite object and that results in needless complexity when handling methods addressed to those sections of the composite object.

No virtual destructor - C++ queries, A destructor is a member function that is invoked automatically when the object goes out of scope or is explicitly destroyed by a call to delete . Using virtual destructors, you can destroy objects without knowing their type — the correct Derived classes do not inherit the destructor of their base class. Virtual inheritance is a C++ technique thatensures only one copy of a base class's member variables areinherited by grandchild derived classes. This feature ismost useful for multiple inheritance , as it makes the virtual base a common subobject for the deriving class andall classes that are derived from it.

Comments
  • Well, the vtable entries would need to point at ~AB, not ~A or ~B...
  • @OliCharlesworth: Ah, that would certainly make more sense.
  • Microsoft C++ docs has a nice example of virtual vs non-virtual destruction order msdn.microsoft.com/en-us/library/6t4fe76c.aspx#Anchor_2
  • "really something you shouldn't be concerned about" Aw, come on, it's interesting to know! :) (even if it varies from implementation to implementation)