_________________
245 стр. Глава 21. Знакомство с виртуальными функциями-членами: настоящие ли они
«Достаточно объявить функцию виртуальной только в базовом классе. Виртуальность наследуется подклассами автоматически. Однако в этой книге я следую стандарту кодирования, в соответствии с которым функции объявляются виртуальными везде.»
[Советы]
«Обратитесь к программе PolymorphicNachos.срр на прилагаемом компакт-диске, чтобы лучше ознакомиться с полиморфизмом.»
[Диск]
►Когда фунция не являтся виртуальной...246
Даже если вы считаете, что некоторая функция вызывается с использованием позднего связывания, это отнюдь не означает, что так и есть на самом деле. Если она не объявлена с теми же аргументами в подклассах, то она не будет переопределяться, независимо от того, объявили ли вы её виртуальной или нет.
В правиле об идентичности объявления есть только одно исключение, которое состоит в том, что если функция-член базового класса возвращает указатель или ссылку на объект базового класса, то переопределяемая функция-член может возвращать указатель или ссылку на объект подкласса. Другими словами, приведённая ниже программа допустима.
class Base
{
public :
/* Возвращает копию текущего объекта */
Base* makeACopy( )
{
/* ...делает всё, что нужно для создания копии */
}
} ;
class Subclass : public Base
{
public :
/* Возвращает копию текущего объекта */
Subclass* makeACopy( )
{
/* ...Делает всё, что нужно для создания копии */
}
} ;
void fn( BaseClass& bс )
{
BaseClass* pCopy = bс.makeACopy( ) ;
/* Функция продолжается... */
}
С практической точки зрения всё естественно: функция копирования makeCopy( ) должна возвращать указатель на объект типа Subclass, даже если она переопределяет Base::makeCopy( ).
_________________
246 стр. Часть 4. Наследование
►Виртуальные особенности...247
При использовании виртуальных функций не следует забывать о некоторых вещах. Во-первых, статические функции-члены не могут быть объявлены виртуальными. Поскольку статические функции-члены не вызываются с объектом, никакого объекта этапа выполнения не может быть, а значит, нет и его типа.
Во-вторых, при указании имени класса в вызове функция будет компилироваться с использованием раннего связывания независимо от того, объявлена она виртуальной или нет.
Например, приведённый ниже вызов обращается к Base::fn( ), поскольку так указал программист, независимо от того, объявлена fn( ) виртуальной или нет.
void test( Base& b )
{
b.base::fn( ) ;
/* Этот вызов не использует позднего связывания */
}
Кроме того, виртуальная функция не может быть встроенной. Чтобы подставить функцию на место её вызова, компилятор должен знать её на этапе компиляции. Таким образом, независимо от способа описания виртуальные функции-члены рассматриваются как не встроенные.
И наконец, конструкторы не могут быть виртуальными, поскольку во время работы конструктора не существует завершённого объекта какого-либо определённого типа. В момент вызова конструктора память, выделенная для объекта, является просто аморфной массой. И только после окончания работы конструктора объект становится экземпляром класса в полном смысле этого слова.
В отличие от конструктора, деструктор может быть объявлен виртуальным. Более того, если он не объявлен виртуальным, вы рискуете столкнуться с неправильной ликвидацией объекта, как, например, в следующей ситуации: