Вопрос деструкторов C ++ - PullRequest
4 голосов
/ 21 мая 2009

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

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()

Ответы [ 8 ]

16 голосов
/ 21 мая 2009

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

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

7 голосов
/ 21 мая 2009

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

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

и аналогично для Derived.

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

4 голосов
/ 21 мая 2009

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

2 голосов
/ 21 мая 2009

Поскольку вы создаете временный тип 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 не является виртуальной.

Редактировать К сожалению, полностью пропустил нарезку, которая происходит, как указано Литб. Вместо этого прочитайте его ответ:)

1 голос
/ 21 мая 2009

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

 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 голосов
/ 22 мая 2009

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

2) Объект temp скопирован в "a"

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

4) возврат EXIT_SUCCESS;

5) "a" уничтожено, поэтому Base :: ~ Base () называется

0 голосов
/ 21 мая 2009

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

0 голосов
/ 21 мая 2009
...