Что происходит при вызове чистой виртуальной функции в конструкторе базового класса в C++
Что проверяют
Знаете ли вы порядок построения объекта и то, как в это время ведёт себя таблица виртуальных функций. Вопрос со ставкой: верный ответ не «вызовется версия базового класса», а «это неопределённое поведение».
Как отвечать
Объект строится снизу вверх: сначала базовая часть, потом производная. Пока работает конструктор базового класса, производной части ещё нет, и указатель на таблицу виртуальных функций смотрит на таблицу базового класса. Поэтому виртуальный вызов из конструктора никогда не уходит в переопределение наследника, даже если создаётся именно наследник.
Дальше зависит от того, что стоит в базовом классе:
- обычная виртуальная функция с телом: вызовется реализация базового класса, тихо и предсказуемо, но не та, которую задумывал автор;
- чистая виртуальная функция: вызывать нечего, и это неопределённое поведение. На практике либо не соберётся, либо программа упадёт в рантайме на заглушке с сообщением про вызов чистой виртуальной функции.
Компиляторы предупреждают о прямом вызове в конструкторе, но косвенный, через вспомогательный невиртуальный метод, они уже не ловят.
Как правильно: не звать виртуальные методы из конструктора вообще. Данные, нужные на старте, передавать параметрами, а если без переопределяемого шага не обойтись, вынести его в отдельный метод инициализации и звать после построения, обычно из фабричной функции.
Где спотыкаются
Отвечают, что сработает реализация базового класса. Для чистой виртуальной функции реализации нет, и осмысленного поведения у такого вызова тоже нет; ожидание тихого значения по умолчанию тут самое опасное.
Забывают про деструктор. При разрушении всё зеркально: к моменту работы деструктора базового класса производная часть уже уничтожена, таблица снова базовая, и вызов оттуда ломается так же.
Прячут вызов за слой. Конструктор зовёт обычный метод базового класса, а тот внутри уже виртуальный: предупреждения компилятора нет, поведение прежнее, и ищется такое потом долго.