Search code examples
c++oopdestructorvtablevirtual-destructor

Order of calling virtual destructors in C++


Well so i have been trying to understand OOP concepts through C++ , however i am not able to get some parts of virtual destructors.

I have written a small snippet :

class A{
    int x;
public: 
    virtual void show(){
        cout << " In A\n"; 
    }
    virtual ~A(){
        cout << "~A\n";
    };
};

class B: public A{
    int y;
public: 
    virtual void show(){
        cout << " In B\n"; 
    }
    virtual ~B(){
        cout << "~B\n";
    };
};

class C: public A{
    int z;
public: 
    virtual void show(){
        cout << " In C\n"; 
    }
    virtual ~C(){
        cout << "~C\n";
    };
};
class E: public A{
    int z;
public: 
    virtual void show(){
        cout << " In E\n"; 
    }
    virtual ~E(){
        cout << "~E\n";
    };
};

class D: public B , public C , public E{
    int z1;
public: 
    virtual void show(){
        cout << " In D\n"; 
    }
    virtual ~D(){
        cout << "~D\n";
    };
};

signed main(){
    // A * a = new A();
    // B *b = new B();
    D *d = new D();
    B *b = d;
    C *c = d;
    E * e = d;
    A * a = new A();
    cout << d << "\n";
    cout << b  << "\n";
    cout  << c << "\n";
    cout << e << "\n";
    delete b;
    // a -> show();

}

On running the code , i get the result as :

0x7f8c5e500000
0x7f8c5e500000
0x7f8c5e500018
0x7f8c5e500030
~D
~E
~A
~C
~A
~B
~A

Now three questions :

  • According to the wikipedia article , virtual_table , it was referred that object c gets an address +8 bytes than that of d and b , what happens in case of e.
  • When i call delete b instead of delete d , also get the same order sequence of virtual destructors , so why is the derived class destructor called
  • The virtual destructors are called only when i delete an object , then how are the vtable and vpointers gets deleted when the program ends ( when i run the code without the delete d the execution just stops without printing anything ).

Solution

  • Your questions in order:

    (1) Yes, pointers to bases refering to objects of derived classes with multiple inheritance may change their numerical value compared to a pointer to the most derived type. The reason is that the base class is a part of the derived class, much like a member, residing at an offset. Only for the first derived class in multi-inheritance this offset can be 0. This is the reason why such pointers cannot be cast with a simple reinterpret_cast().

    (2) b points to an E which also is-an A.

    Exactly that is what being virtual means for a member function: The code generated by the compiler inspects the object pointed to at run time and calls the function defined for the actual type of the object (which is an E), as opposed to the type of the expression used to access that object (which is B). The type of the expression is fully determined at compile time; the type of the actual complete object is not.

    If you do not declare a destructor virtual the program may behave as you perhaps expected: The compiler will create code which simply calls the function defined for the type of the expression (for B), without any run-time look-ups. Non-virtual member function calls are slightly more efficient; but in the case of destructors as in your case the behavior is undefined when destroying through a base class expression. If your destructor is public it should be virtual because this scenario could happen.

    Herb Sutter has written an article about virtual functions including virtual destructors that's worth reading.

    (3) The memory, including dynamically allocated memory, is released and made available again for other uses by modern standard operating systems when the program has exited. (This may not be the case in old operating systems or freestanding implementations, if they offer dynamic allocation.) Destructors of dynamically allocated objects, however, will not be called, which may be a problem if they hold resources like database or network connections which should better be released.