В чем разница между статическим и динамическим полиморфизмом в коде
Что проверяют
Знаете ли вы, какими средствами C++ реализует оба вида полиморфизма и во что каждый обходится. Почти всегда следом просят объяснить, как устроен вызов виртуальной функции и что такое CRTP.
Как отвечать
Разница в моменте, когда выбирается конкретная реализация:
- статический: выбор на этапе компиляции. Механизмы: перегрузка функций и операторов, шаблоны, CRTP (
class Derived : public Base<Derived>), а в C++20 концепты ограничивают шаблон требованиями к типу. Вызов прямой, и компилятор может его встроить; - динамический: выбор во время выполнения через
virtual. У класса с виртуальными функциями есть таблицаvtable, в объекте лежит указатель на неё, и вызов через указатель или ссылку на базу идёт косвенно.
Цена динамического полиморфизма не столько в косвенном переходе, сколько в том, что компилятор не может встроить вызов и оптимизировать сквозь него. Плюс указатель в каждом объекте и обычно куча: разнородные объекты хранят как std::vector<std::unique_ptr<Base>>. Если класс помечен final, компилятор часто девиртуализирует вызов.
Цена статического: каждая инстанциация шаблона это отдельный код, бинарник растёт, сборка идёт дольше, а разные типы в один контейнер без обёртки не сложить. Промежуточный вариант: std::variant с std::visit, закрытый набор типов без наследования и без кучи.
Если набор типов известен при сборке и важна скорость во внутреннем цикле, подойдут шаблоны. Если нужны плагины, расширение без перекомпиляции или стабильный ABI библиотеки, подойдут виртуальные функции.
Где спотыкаются
Забывают виртуальный деструктор в базовом классе. Без него delete объекта наследника через указатель на базу это неопределённое поведение.
Вызывают виртуальную функцию из конструктора или деструктора и ждут версию наследника. В этот момент динамический тип объекта ещё или уже базовый, сработает реализация базы.
Считают виртуальный вызов «в разы медленнее» всегда. Разница заметна в горячих циклах с миллионами вызовов, в обычной бизнес-логике её не видно, и выбирать надо по архитектуре.