#новичкам
Возможно САМЫЙ популярный вопрос на собеседованиях на джунов и мидлов. Оно и справедливо в принципе: очень простой и понятный вопрос, но ответ на него требует хорошего уровня понимания ООП в принципе и как оно конкретно работает в плюсах. Динамический полиморфизм, наследование, порядок вызовов конструкторов и деструкторов - да, это база. Но именно ее и нужно проверить у начинающих и продолжающих разработчиков.
Обычно они заходят немного издалека и просят вангануть, что выведется в консоль для примерно такого кода:
struct Resource {
Resource() { std::cout << "Resourse has been acquired\n";}
~Resource() { std::cout << "Resource has been released\n";}
};
struct Base {
Base() { std::cout << "Base Constructor Called\n";}
~Base() { std::cout << "Base Destructor called\n";}
};
struct Derived1: Base {
Derived1() {
ptr = std::make_unique<Resource>();
std::cout << "Derived constructor called\n";
}
~Derived1() {std::cout << "Derived destructor called\n";}
private:
std::unique_ptr<Resource> ptr;
};
int main() {
Base *b = new Derived1();
delete b;
}
Вроде ничего сложного, но вот надо все штуки вспомнить, как там объекты создаются, в каком порядке что вызывается. Для начинающих тут часто затупки начинаются.
В коде вроде все хорошо написано и невнимательный кандидат может выдать вот это:
Base Constructor Called
Resourse has been acquired
Derived constructor called
Derived destructor called
Resource has been released
Base Destructor called
Вот тут-то его и подловили! На самом деле никакого деструктора наследника вызвано не будет и соответственно ресурсы не освободятся. Интервьюер дает наводку посмотреть на деструктор базового класса. И кандидат с красным лицом кричит: "Деструктор - невиртуальный! По указателю на базовый класс вызовется сразу деструктор базового класса, а деструктор дочернего не вызовется. Будет утечка памяти". Его так на курсах научили говорить. И дальше он выдает правильный вывод программы.
И тут интервьюер говорит: "А что будет, если наследник не будет содержать никаких полей? Какие проблемы будут у этого кода?".
И молодой разработчик в ступоре: он же знает, что невиртуальный деструктор приводит к утечкам. Но тут вроде как и утекать нечему. И говорит, что вроде как и проблем не будет.
Естественно, это неправда.
Если с виду ничего плохого не может произойти и даже при запуске программы ничего плохого не происходит - это не значит, что в программе нет проблем. Стандарт говорит:
if the static type of the object to be deleted
is different from its dynamic type, the static type
shall be a base class of the dynamic type of
the object to be deleted and the static type
shall have a virtual destructor or
the behavior is undefined.
Отсутствие виртуального деструктора при удалении через базовый класс приводит к неопределенному поведению. И точка. Можете даже больше не упоминать утечки. Потому что может память утечь, а может и Пентагон задудосится от такой программы. Никто не знает.
Для корректного поведения полиморфных объектов и вызова деструктора дочернего класса вам обязательно нужен виртуальный деструктор базового класса.
Часто встречал эту проблему у младших разработчиков, да и сам я спотыкался на этом. Но теперь наши подписчики вооружены и опасны!
Stay armed. Stay cool.
#cppcore #interview