Разница в том, что вы можете вызывать виртуальную функцию в любом экземпляре, производном от Base
. Элемент notVirtualCall()
не существует в Base
и не может быть вызван без предварительного определения точного динамического типа объекта.
Следствием этого различия является то, что vtable базового класса включает в себя слот для virtualCall()
, который содержит указатель функции на правильную функцию для вызова. Таким образом, виртуальный вызов просто преследует указатель vtable, включенный в качестве первого (невидимого) члена всех объектов типа Base
, загружает указатель из слота, соответствующего virtualCall()
, и вызывает функцию, стоящую за этим указателем.
Когда вы делаете dynamic_cast<>
, класс Base
, напротив, не знает во время компиляции, что другие классы в итоге получат из него. Следовательно, он не может включать в свой vtable информацию, которая облегчает разрешение dynamic_cast<>
. Именно недостаток информации делает реализацию dynamic_cast<>
более дорогой, чем вызов виртуальной функции. dynamic_cast<>
должен на самом деле искать в дереве наследования реального объекта, чтобы проверить, найден ли тип назначения приведения среди его баз. Это работа, которую избегает виртуальный вызов.