2014-10-24 5 views
-7

В коде ниже, почему дескриптор ~Derived() называется автоматически?виртуальный деструктор в C++

#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; 
} 
+4

Предпочитаете ли вы, чтобы этого не было? – chris

+9

Я не отвечаю на вопрос. Вы спрашиваете, почему удаление объекта 'Derived' должно вызывать деструктор' Derived'? Потому что это значит, что объект Derived должен быть уничтожен. Или вы спрашиваете об этом механизме, чтобы он корректно работал с указателем «Base»? Это точно так же, как и любой другой вызов виртуальной функции. –

+1

Я просто хочу знать механизм, почему вызывается деструктор drived? – Sajid

ответ

3

Поскольку ваш деструктор базового класса является виртуальным

virtual ~Base(); 

вызова для удаления по указателю на результаты базового класса в виртуальном вызове деструктор и как любой виртуальный вызов отправляются в соответствии функции в полученном класс. Это не только хорошо, но и необходимо: в противном случае поведение не определено.

Это имеет решающее значение для производных классов, деструктор которых не является пустой функцией. Не виртуальный вызов в противном случае привел бы к вызову деструктора базового класса, утечка производных ресурсов и т. Д.

+0

В противном случае поведение не определено. В любом случае это приведет к сбою машины, например. –

+1

@JamesKanze true, спасибо, добавлено – 4pie0

0

Потому что он позволяет обрабатывать любой объект Base (который может быть фактически Derived) в качестве объекта, который вы можете удалить.

В этом случае, если delete pBase не вызывал Derived деструктора, данные, проведенные m_pnArray никогда не удаляются, то есть «утечки памяти» будет происходить.

0

Когда вы звоните

delete pBase; 

Он смотрит на функции таблицы PBase виртуального найти подходящий деструктор, чтобы начать разматывать на, и это находит Derived :: ~ Derived, а затем работает свой путь вниз по стеку.

1

Если у вас есть хотя бы одна функция virtual в классе, то компилятор создает отдельную таблицу для класса с перечислением указателей на функции-члены. Рассмотрим:

struct Base 
{ 
    virtual ~Base() { }; 

    int n_; 
}; 

В псевдокод вы можете себе представить, что компилятор добавляет:

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

Затем, когда у вас есть реальный объект типа Base будет иметь дополнительный скрытый элемент данных, который указывает к Base::__virtual_dispatch_table (далее "ВДТ"):

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

Теперь, если у вас есть Base* p и delete p;, компилятор говорит «эй - это virtual - Я не буду жёстко вызов Base::~Base, вместо этого я генерировать код, который делает что-то вроде этого псевдокода:

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

Почему вы хотите сделать все что? Потому что, когда вы приходите вместе с Derived объекта ...

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

... компилятор может создать отдельную виртуальную таблицу отправки ...

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

...ANDD выложу память производный объекта, как это:

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

Обратите внимание, что __p_vdt находится в том же относительном положении в пределах расположения объекта, но теперь указывает на виртуальную таблицу диспетчеризации в Derived класса?

Теперь, если вы создаете Base* к derived, точно такой же код, необходимый для вызова деструктора для Base объекта, который - в случае, если вы потеряли след - был ...

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

. .. может быть запущен, но в итоге будет использоваться значение объекта __p_vdt значение Derived::__virtual_dispatch_table, а также поиск деструктора класса Derived.

+0

vtables - это деталь реализации. –

+0

@LightnessRacesinOrbit Да ... «Я просто хочу знать механизм» - я дал примерную иллюстрацию универсально используемого механизма реализации, хотя, конечно, другие возможны. –

Смежные вопросы