2009-05-21 3 views
4

Что касается примера кода ниже, почему деструктор для базового класса называется дважды?Вопрос о деструкторах C++

class Base { 
public: 
    Base() { 
     std::cout << "Base::Base()" << std::endl; 
    } 

    ~Base() { 
     std::cout << "Base::~Base()" << std::endl; 
    } 
}; 

class Derived : public Base { 
public: 
    Derived() { 
     std::cout << "Derived::Derived()" << std::endl; 
    } 

    ~Derived() { 
     std::cout << "Derived::~Derived()" << std::endl; 
    } 
}; 

int main() { 
    Base a = Derived(); 
    return EXIT_SUCCESS; 
} 

Вот пример вывода при выполнении программы:

Base::Base() 
Derived::Derived() 
Derived::~Derived() 
Base::~Base() 
Base::~Base() 
+0

Исправлено форматирование вашего кода. Первый раз, когда я видел, так сильно испортил форматирование. ;) – jalf

+2

Чтобы узнать, что происходит дальше, добавьте конструктор копирования для Base. Затем вы увидите недостающую конструкцию, связанную с несогласованными деструкторами. –

+0

Да, хороший совет. При тестировании таких вещей всегда проверяйте, что * all * constructors (плюс оператор присваивания) отслеживаются. В противном случае вы можете пропустить половину того, что происходит. – jalf

ответ

16

Что происходит, называется нарезкой. Вы инициализируете объект типа Base с объектом типа Derived. Так как любой объект типа Derived также имеет объект типа Base, содержащий (называемый «суб-объект базового класса»), в программе будут два объекта Base и один объект Derived. Объект Derived (и его под-объект базового класса типа Base) существует только для времени инициализации, тогда как оставшийся объект Base существует до конца main.

Поскольку есть два базовых объекта и один производный объект, вы также увидите, как запускается еще один деструктор базы.

+0

Doh, нарезка мне вообще не пришла, просто невращательный деструктор. Хорошо поймал. – jalf

+0

Точно. Компилятор, синтезированный конструктор копирования, применяется для инициализации базового объекта с помощью производного объекта и, конечно же, имеет место предметная нарезка. –

0

Вам нужен virtual destructor.

+2

Неправильно. это не имеет ничего общего с бхавием, который видит вопрошающий. виртуозный деструктор требуется только в том случае, если вы удаляете объект класса Derived с помощью указателя Base, чего здесь нет. – 2009-05-21 18:05:25

4

Когда вы говорите Derived() в main(), он создает временный объект, который затем копируется в объект a. Следовательно, есть два объекта, из-за которых деструктор вызывается дважды. Кроме того, как указывали другие, деструктор базового класса должен быть виртуальным.

7

Используется копировальный конструктор. Если вы хотите посмотреть, что происходит, запишите также конструктор копирования:

Base(const Base &) { 
     std::cout << "Base::Base(const Base &)" << std::endl; 
    } 

и аналогичным образом для производного.

Обратите внимание, что это НИЧЕГО не связано с тем, что деструкторы не являются виртуальными.

+0

Кроме того, выведите этот указатель, чтобы продемонстрировать, что есть два объекта (и срез объектов). – dave4420

+0

Нейл, вот и все. Конструктор копирования синтезируется b компилятором и используется для инициализации базового объекта. Когда базовый объект выходит из области видимости, деструктор вызывается дважды. –

+0

Обработка объектов также происходит, поскольку конструктор базовой копии принимает объект Derived. –

2

Потому что вы создаете временное типа Derived перед копированием a с ним. Так что это в основном то, что происходит:

Derived d(); // Your temporary of type Derived is created 
Base a(d); // The temporary is used to call a's copy constructor 
d.Derived::~Derived(); // The temporary is destroyed, calling both ~Derived and ~Base 
a.Base::~Base(); // The nonvirtual destructor on a is called, so ~Base is called, but not ~Derived 

Так кроме ненужного копирования в начале (что компилятор может оптимизировать прочь), фактическую ошибки является то, что ~ Base не является виртуальной.

Редактировать Ой, полностью пропустил срез, который имеет место при указании на указанную лампочку. Вместо этого прочитайте его ответ:

0

У вас есть одна переменная стека, а одна временная - общая сумма двух объектов, построенных - так что логично, чтобы деструктор вызывался дважды.

1

Добавление в дальнейшем будет сделать программу более понятной:

Base(const Base& base){ 
     std::cout << "Base::Base(const Base& base)" << std::endl; 
} 

компилятор автоматически создает конструктор копирования для вас.Определяя его самостоятельно (и, добавив в печати), вы можете увидеть, что количество конструкторов и деструкторов матч

Base::Base() 
Derived::Derived() 
Base::Base(const Base& base) 
Derived::~Derived() 
Base::~Base() 
Base::~Base() 
0

1) Временный объект производного типа построен (Derived :: Derived() и база :: База() называются)

2) объект темп копируется в "а"

3) временный объект уничтожается (Derived :: ~ Derived() и Base :: ~ Base() называются)

4) возвращение EXIT_SUCCESS;

5) «a» разрушен, поэтому Base :: ~ Base() называется