virtual destructor in c++

时光怂恿深爱的人放手 提交于 2019-12-13 11:30:37

问题


In the code below, why is the ~Derived() destructor called automatically?

#include<iostream>
using namespace std;
class Base
{
public:
    virtual ~Base()
    {
        cout << "Calling ~Base()" << endl;
    }
};

class Derived: public Base
{
private:
    int* m_pnArray;

public:
    Derived(int nLength)
    {
        m_pnArray = new int[nLength];
    }

    virtual ~Derived()
    {
        cout << "Calling ~Derived()" << endl;
        delete[] m_pnArray;
    }
};

int main()
{
    Derived *pDerived = new Derived(5);
    Base *pBase = pDerived;
    delete pBase;

    return 0;
}

回答1:


Because your base class destructor is virtual

virtual ~Base();

the call to delete on a pointer to a base class results in virtual call to destructor and as any virtual call is dispatched to matching function in derived class. It is not only good, but necessary: otherwise the behavior is undefined.

This is crucial for a derived classes which destructor is not an empty function. Non-virtual call would otherwise result in calling base class destructor, derived resources being leaked, etc.




回答2:


When you have at least one virtual function in a class, then the compiler creates a single table for the class listing the member function pointers. Consider:

struct Base
{
    virtual ~Base() { };

    int n_;
};

In pseudo-code you can imagine the compiler adding:

void* Base::__virtual_dispatch_table[] = { (void*)&Base::~Base };

Then, when you have an actual object of type Base it will have an extra hidden data member that points to the Base::__virtual_dispatch_table (the "VDT"):

Variable definition       Memory layout
-------------------       -------------
Base myBase;              int n_;
                          void** __p_vdt = Base::__virtual_dispatch_table;

Now, if you have a Base* p and delete p;, the compiler says "hey - it's virtual - I won't hardcode a call to Base::~Base, instead I'll generate code that does something like this pseudo-code:

void (Base::*p_destructor) = p->__p_vdt[0]
*p_destructor(p);   // "p" will provide the "this" value while the destructor runs

Why would you want to do all that? Because when you come along with a Derived object...

class Derived: public Base
{
private:
    int* m_pnArray;
    ...

...the compiler can create a separate virtual dispatch table...

void* Derived::__virtual_dispatch_table[] = { (void*)&Derived::~Derived };

...andd lay out the Derived object's memory like this:

Variable definition       Memory layout
-------------------       -------------
Derived derived;          int n_;
                          void** __p_vdt = Derived::__virtual_dispatch_table;
                          int* m_pnArray;

Notice that the __p_vdt is in the same relative location within the object layout, but now points to the Derived class's virtual dispatch table?

Now, if you create a Base* to derived, the exact same code needed to call the destructor for a Base object, which - in case you've lost track - was...

void (Base::*p_destructor) = p->__p_vdt[0]
*p_destructor(p);   // "p" will provide the "this" value while the destructor runs

...can be run but will end up using the Derived object's __p_vdt value of Derived::__virtual_dispatch_table, and finding the Derived class's destructor.




回答3:


Because it allows you to treat any Base object (which may in fact be a Derived) as an object that you can delete.

In this case, if delete pBase didn't call the Derived destructor, the data held by m_pnArray would never get deleted, i.e. a "memory leak" would occur.




回答4:


When you call

delete pBase;

It looks at the virtual function table of pBase to find the appropriate destructor to begin unwinding at, and it finds Derived::~Derived and then works its way down the stack.



来源:https://stackoverflow.com/questions/26547686/virtual-destructor-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!