История вопроса:
В C++ появилась поддержка объектно-ориентированного программирования, базового для современных языков. Для реализации полиморфизма использовались виртуальные функции. Это позволяло вызывать нужную реализацию метода на этапе выполнения, а не только компиляции, что критично для архитектуры с наследованием.
Проблема:
Распространённая ошибка — путаница между статическим и динамическим вызовом методов, забытые виртуальные деструкторы, неправильная работа с наследованием (например, object slicing, вызов base версии вместо override). Часто путают, когда реально работает полиморфизм.
Решение:
Виртуальная функция объявляется с помощью ключевого слова virtual в базовом классе и может быть переопределена (override) в производном. Если вызвать функцию по указателю или ссылке на базовый класс, будет выполнена версия из производного класса.
Пример кода:
struct Base { virtual void foo() { std::cout << "Base::foo "; } }; struct Derived : Base { void foo() override { std::cout << "Derived::foo "; } }; void call(Base& b) { b.foo(); } int main() { Derived d; call(d); // Выведет Derived::foo }
Ключевые особенности:
Работает ли полиморфизм при передаче объекта по значению?
Нет. Передача по значению приводит к "slicing" — копируется только часть, соответствующая типу параметра (обычно базовый класс), полиморфизм отключается.
Пример кода:
void call(Base b) { b.foo(); } // всегда вызов Base::foo
Нужно ли объявлять деструктор виртуальным в базовом классе?
Да, если предполагается удаление производных объектов через указатель на базовый класс. Иначе произойдёт утечка памяти или незакрытие ресурсов.
Пример кода:
struct Base { virtual ~Base() {} };
Что произойдёт, если не использовать ключевое слово override в дочернем классе?
Если в производном классе не указать override, но ошибочно изменить сигнатуру функции (например, пропустить const или ошибиться в параметрах), функция не переопределяет виртуальную, создаётся новая, и полиморфизм не работает как ожидается.
Программист не объявил деструктор базового класса виртуальным; удаление массива объектов через базовый указатель привело к утечке памяти.
Плюсы:
Минусы:
Был объявлен виртуальный деструктор; использовались только ссылки/указатели на базовый тип. Полиморфизм работал корректно.
Плюсы:
Минусы: