Вызов переопределенных методов в конструкторе / деструкторе
Едва познакомившись с поведением динамического полиморфизма можно придумать достаточно много разных способов его применения. Здорово, если это действительно так! 😃
Вот, например, теперь мы можем в базовом классе выполнить переопределенный виртуальный метод наследованного класса:
Далее следует интересный вопрос, что будет если вызвать метод
вывелось из base_t. Почему?
Такое ограничение при вызове переопределенных методов связано с жизненным циклом объекта и порядком выполнения конструкторов и деструкторов:
При вызове переопределенного метода из конструктора или деструктора, будет вызвана реализация из этого же класса. Такое поведение явно определено в целях безопасности: иначе ваши переопределенные виртуальные методы могут использовать неинициализированную память наследников.
Сделал живой пример 2, в котором постарался наглядно продемонстрировать, что при прямом вызове перегруженной реализации мы бы прочитали невалидные данные. Если что, так делать нельзя 😉 Тут важно просто понять мотивацию ограничений.
Желаю всем, чтобы вы всегда получали ожидаемый результат исполнения ваших программ!
#cppcore
Едва познакомившись с поведением динамического полиморфизма можно придумать достаточно много разных способов его применения. Здорово, если это действительно так! 😃
Вот, например, теперь мы можем в базовом классе выполнить переопределенный виртуальный метод наследованного класса:
struct base_tБазовый класс знает, что метод
{
virtual const char* name() const
{
return "I'm base!";
}
void call_me()
{
std::cout << name() << std::endl;
}
virtual ~base_t() = default;
};
struct derived_t : base_t
{
const char* name() const override
{
return "I'm derived!";
}
};
void main()
{
derived_t object;
// Выводит "I'm derived!"
object.call_me();
}
name()
- виртуальный, поэтому используя встроенный механизм виртуальных таблиц, будет вызвано его переопределение.Далее следует интересный вопрос, что будет если вызвать метод
call_me
из конструктора или деструктора?virtual ~base_t()Виртуальный деструктор будет вызван в момент выхода тела
{
call_me();
}
main
. Вероятно, что вы ожидаете увидеть имя из derived_t
, ведь метод был переопределен. Давайте проверим в живом примере 1. Спойлер: Такое ограничение при вызове переопределенных методов связано с жизненным циклом объекта и порядком выполнения конструкторов и деструкторов:
Constructors:Следовательно, и состояние данных в классах тоже зависит от порядка инициализации / разрушения классов.
base() -> derived_1() -> derived_2() -> ...;
Destructors:
... -> ~derived_2() -> ~derived_1() -> ~base();
При вызове переопределенного метода из конструктора или деструктора, будет вызвана реализация из этого же класса. Такое поведение явно определено в целях безопасности: иначе ваши переопределенные виртуальные методы могут использовать неинициализированную память наследников.
Сделал живой пример 2, в котором постарался наглядно продемонстрировать, что при прямом вызове перегруженной реализации мы бы прочитали невалидные данные. Если что, так делать нельзя 😉 Тут важно просто понять мотивацию ограничений.
Желаю всем, чтобы вы всегда получали ожидаемый результат исполнения ваших программ!
#cppcore