В случае переопределяемых функций компилятор умеет отличать один
вызов от другого по типу их аргументов. Используя эту информацию, он
"жестко" связывает коды программы с соответствующими функциями элементами. С другой стороны, бывает необходимо отличить один вызов от
другого, при наличии аргументов одного типа на этапе выполнения, и
обеспечить потомки класса разными версиями функций базового класса.
Именно использование ключевого слова virtual приводит к отсрочке связывания, и вызову нужной функции на этапе выполнения.
Виртуальная функция элемент - это функция, которая будучи описана в потомках, замещает собой соответствующую функцию элемент везде -
даже в предке, если она вызывается для потомка. В отличии от раннего
связывания с использованием переопределяемых функций элементов, виртуальные функции элементы должны иметь аргументы одного типа.
Синтаксис определения виртуальных функций элементов очень прозрачный: добавьте слово virtual к первому определению функции элементу:
virtual void Show();
virtual void Hide();
Внимание! Только встроенные функции элементы могут быть объявлены как виртуальные. Как только функция объявлена виртуальной, она не
может быть переопределена ни в каком наследуемом классе с однотипным
перечнем аргументов, но с другим типом возвращаемого значения. Если
вы переопределяете Show с тем же перечнем однотипных аргументов и таким же типом возвращаемого значения, то новая функция Show автоматически становится виртуальной, независимо от того, используется ключевое слово virtual или нет. В этом случае говорят, что новая виртуальная Show замещает Show в своем базовом классе.
Вы можете свободно переопределять Show с другим перечнем разнотипных аргументов (изменяя при этом тип возвращаемого значения или
нет), но виртуальный механизм не задействуется для такой версии Show.
Какая именно функция элемент Show будет вызвана - зависит только
от класса объекта, для которого вызывается Show, даже если вызов производится через указатель на базовый класс. Например,
Circle ACircle
Point* APoint_рointer = &ACircle; // указатель на Circle,
// которому присваивается
// значение указателя на
// базовый класс, Point
APoint_рointer->Show(); // вызывает Circle::Show!
Так как вызов невиртуальной функции-элемента выполняется несколько быстрее, чем виртуального, то в общем случае рекомендуется,
когда не встает вопрос о расширяемости, но существенное значение имеет производительность, пользоваться обычными функциями элементами. В
противном случае, нужно пользоваться виртуальными.