Вы должны быть немного озадачены динамической отправкой.
Часто говорят, что "динамическая отправка происходит только тогда, когда вы делаете вызов через указатель или через ссылку". Формально это утверждение является полностью ложным и вводящим в заблуждение.
Динамическая диспетчеризация в C ++ происходит всегда при вызове виртуальной функции, с одним и единственным исключением: динамическая диспетчеризация отключается, когда вы используете полностью определенное имя цели функция. Например:
some_pointer->SomeClass::some_function(); // fully-qualified name
В приведенном выше коде вызов будет отправляться статически, даже если some_function
является виртуальной функцией. С языковой точки зрения, есть нет других способов избежать динамической отправки, т. Е. В во всех других случаях все вызовы виртуальных функций отправляются динамически . Что вы используете: указатель, ссылка, непосредственный объект - не имеет значения, отправка по-прежнему динамическая . Откуда вы вызываете функцию: из конструктора / деструктора или откуда-то еще - не имеет значения, отправка по-прежнему динамическая . И я повторяю: так обстоит дело с точки зрения самого языка C ++. Вот как работает «абстрактная машина C ++».
Однако на практике происходит то, что во многих случаях динамическая диспетчеризация может быть заменена статической диспетчеризацией, поскольку компилятор заранее знает динамический тип объекта во время компиляции и, следовательно, знает цель отправка. В таких случаях имеет смысл вызывать целевую функцию напрямую, а не использовать более дорогой механизм динамической отправки. Тем не менее, это не что иное, как оптимизация. К сожалению, некоторые люди ошибочно принимают за оптимизацию поведения, предписанного языком, предлагая такие бессмысленные выражения, как «динамическая диспетчеризация происходит только тогда, когда вы делаете вызов через указатель или ссылку».
В вашем конкретном случае отправка является динамической. Поскольку в вашем случае компилятор не знает динамические типы задействованных объектов, он не может оптимизировать его в статическую диспетчеризацию, поэтому ваш код «работает как задумано».
P.S. Предвосхищая возможные вопросы о чем-то, что я сказал выше: Динамическая диспетчеризация для вызовов, сделанных из конструкторов / деструкторов, ограничена текущим динамическим типом объекта, поэтому в простых случаях компилятор может (и легко) оптимизировать их в статическая отправка. Это причина другой популярной городской легенды, которая утверждает, что виртуальные вызовы от конструкторов / деструкторов разрешаются статически. В действительности, в общем случае они разрешаются динамически, как и должны (опять же, наблюдая за текущим динамическим типом объекта).