Назад к вопросам
Middle+
85
questionbank

Какие тонкости существуют при вызове виртуальных функций из конструктора или деструктора?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

При вызове виртуальной функции из конструктора или деструктора полиморфизм не работает так, как обычно.

Из конструктора:

  • Будет вызвана реализация виртуальной функции, соответствующая типу текущего конструктора (т.е. класса, чей конструктор выполняется). Реализации из производных классов не будут доступны, так как объекты производных классов еще не полностью построены.

Из деструктора:

  • Аналогично, будет вызвана реализация виртуальной функции, соответствующая типу текущего деструктора. После выполнения деструктора базового класса, часть объекта, соответствующая производному классу, уже разрушена.

Почему так?

  • На этапе конструирования объекта производного класса, его части, унаследованные от базовых классов, создаются последовательно. Пока конструктор производного класса не завершился, объект еще не является полноценным экземпляром производного класса.
  • На этапе деструктирования происходит обратный процесс: сначала вызывается деструктор производного класса, затем деструкторы базовых классов в порядке, обратном конструированию. К моменту вызова деструктора базового класса, часть объекта, соответствующая производному классу, уже уничтожена.

Пример:

#include <iostream>

class Base {
public:
    Base() {
        // Вызов виртуальной функции из конструктора
        // Будет вызвана Base::greet()
        greet();
    }
    virtual ~Base() {
        // Вызов виртуальной функции из деструктора
        // Будет вызвана Base::greet()
        greet();
    }
    virtual void greet() {
        std::cout << "Привет из Base" << std::endl;
    }
};

class Derived : public Base {
public:
    Derived() : Base() {}
    ~Derived() override {}
    virtual void greet() override {
        std::cout << "Привет из Derived" << std::endl;
    }
};

int main() {
    // При создании объекта Derived, конструктор Base::Base()
    // будет вызывать Base::greet()
    // Затем конструктор Derived::Derived() завершится

    // При удалении объекта Derived, деструктор Derived::~Derived() будет вызван,
    // затем деструктор Base::~Base() будет вызван,
    // который вызовет Base::greet()
    Derived* obj = new Derived();
    delete obj;

    return 0;
}

Вывод программы будет:

Привет из Base
Привет из Base

Вместо ожидаемого полиморфного поведения (Привет из Derived).

Следствия:

  • Избегайте вызова виртуальных функций из конструкторов и деструкторов, если ожидается полиморфное поведение на основе типа производного класса.
  • Если необходимо выполнить действия, зависящие от типа объекта, в процессе конструирования/деструктирования, рассмотрите другие подходы, например, вызов не виртуальных функций или передачу информации через параметры конструктора.

Это важное правило для обеспечения корректной работы объектов в C++, особенно при наследовании.