2015-12-17 3 views
3

Я пытаюсь понять порядок, в котором конструкторы и деструкторы вызываются путем написания кода примера и попытки следовать потоку программы. В большинстве случаев я мог понять (с помощью Google, где это необходимо). Однако, в одном конкретном случае, я ударил немного дорожного блока.Порядок вызовов конструктора/деструктора при использовании наследования

Это программа Я использую:

#include <iostream> 
class baseC 
{ 

public: 
     baseC() { std::cout << "Calling constructor of base class: " << std::endl; } 
     virtual char const * getName(){ return "Base Class";} 
     ~baseC(){ std::cout << "Calling destructor of base class: " << std::endl;} 
}; 

class childC : public baseC 
{ 
public: 
     childC() { std::cout << "Calling constructor of child class: " << std::endl; } 
     char const * getName(){ return "Child Class";} 
     ~childC(){ std::cout << "Calling destructor of child class: " << std::endl; } 
}; 

int main() 
{ 
     baseC c3 = childC(); 
     std::cout << c3.getName() << std::endl; 
} 

Это выход я получаю:

$ g++ test_vd_se.cpp -o test; ./test 
Calling constructor of base class: 
Calling constructor of child class: 
Calling destructor of child class: 
Calling destructor of base class: 
Base Class 
Calling destructor of base class: 

Компилятор кажется сначала создать базовый класс и класс ребенка (это, как ожидается,), однако он продолжает уничтожать оба класса, и тем не менее он может вызывать функцию-член из базового класса и продолжает уничтожать базовый класс еще раз.

Я был бы признателен, если бы кто-нибудь мог объяснить, почему функции вызываются в этом порядке.

+2

Не забудьте объявить деструкторы базового класса как виртуальные. В противном случае удаление с помощью базового указателя не вызовет производные деструкторы. –

+0

Самое любопытное, что мы не наблюдали * дважды * предложение 'Calling constructor базового класса:'. См. Мой ответ для объяснения. –

ответ

3

Проблема здесь в том, что вы нарезаете объект.

baseC c3 = childC(); 

собирается создать временный childC, а затем скопировать, что объекты в c3. Вот почему вы видите

Calling constructor of base class: // create base part of temporary 
Calling constructor of child class: // create temporary 

// the copy happens here but you do not output when copying 

Calling destructor of child class: // destroy base part of temporary 
Calling destructor of base class: // destroy temporary 

Корректный способ сделать это - использовать умный указатель. Если изменить main() к

int main() 
{ 
     auto c3 = std::make_unique<childC>(); 
     std::cout << c3->getName() << std::endl; 
} 

Или, если у вас нет доступа к смарт-указатели:

int main() 
{ 
     baseC* c3 = new childC(); 
     std::cout << c3->getName() << std::endl; 
     delete c3; 
} 

Вы получаете:

Calling constructor of base class: 
Calling constructor of child class: 
Child Class 
Calling destructor of child class: 
Calling destructor of base class: 

Live Example

Мы также должны сделать ~baseC()virtual, поэтому вызывается правильный деструктор.

virtual ~baseC(){ std::cout << "Calling destructor of base class: " << std::endl;} 

Вы также отметить, что теперь Child Class печатается вместо Base Class, так как теперь у нас есть указатель динамической диспетчеризации пинки в и вызывает правильную виртуальную функцию.

+0

Не забудьте удалить объект после использования, если вы добавили управление динамической памятью в исходный код. –

+0

@Revolver_Ocelot Добавил это. Благодарю. – NathanOliver

+0

спасибо! С ответами выше я могу лучше понять, что происходит. –

0

«аномалия» происходит от следующего присваивания:

baseC c3 = childC(); 

где вы сначала создать временный childC, вызывая, чтобы конструкторы сверху вниз:

Calling constructor of base class: 
Calling constructor of child class: 

Тогда присваивание место, поэтому создается объект baseC. Но на этот раз это не ваш конструктор, который вызывается, но конструктор копии по умолчанию . Именно поэтому мы не наблюдали Calling constructor of base class: (для строительства объекта c3).Чтобы доказать это, попробуйте добавить копию-конструктор в классе BASEC:

baseC(const baseC& other) { std::cout << "Calling Copy-constructor of base class: " << std::endl; } 

И с той же основной функцией, вы будете наблюдать фразу дважды на выходе:

Calling constructor of base class: 
Calling constructor of child class: 
**Calling copy-constructor of base class:** 
Calling destructor of child class: 
Calling destructor of base class: 
Base Class 
Calling destructor of base class: 

Наконец, временный дочерний объект уничтожается, поэтому деструкторы вызываются снизу вверх.

Calling destructor of child class: 
Calling destructor of base class: 

Теперь baseC объект c3 по-прежнему существует, вызывается метод GetName(), который выводит:

Child Class 

И тогда, когда переменная c3 выходит из области видимости (конец main()), c3 :

Calling destructor of base class: 

И, наконец, все будет по-другому с baseC& c3 = ChildC(); (компилируется с VS2015, я не уверен, соответствует ли он стандарту C++ 14), который не создает два объекта, а только один. Последовательность будет тогда:

contruction of baseC 
contruction of childC 
destruction of childC 
destruction of baseC 

Наконец, всегда безопаснее и хорошая практика, чтобы объявлять деструкторы как виртуальные.

+0

Ваше решение не будет работать: http://coliru.stacked-crooked.com/a/67884429595bf87d – NathanOliver

+0

@NathanOliver отлично компилируется с VS2015;) –

+0

Это vecause VS215 имеет нестандартное расширение, которое позволяет не 'const' Рекомендации. Это единственный компилятор, который поддерживает этот AFAIK – NathanOliver

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