«Я считаю это объяснение глупым, и мне оно нравится не более чем вам, так что просто выучите и живите с ним. Для этого объяснения есть причина, если не оправдание. Каждая виртуальная функция должна иметь свою ячейку в специальной таблице, в которой содержится адрес функции. Так вот: ячейка для чисто виртуальной функции содержит нуль.»
[Технические подробности]
Абстрактный класс не может быть реализован; другими словами, вы не можете создать объект абстрактного класса. Например, приведённое ниже объявление некорректно.
void fn( )
{
Account acnt( 1234, 100.00 ) ; /* Это некорректно */
acnt.withdrawal( 50 ) ; /* Куда, по-вашему, должен обращаться этот вызов? */
}
Если бы такое объявление было разрешено, конечный объект оказался бы незавершённым, поскольку был бы лишён некоторых возможностей. Например, что бы выполнял приведённый в этом же объявлении вызов? Помните, функции Account::withdrawal( ) не существует.
Абстрактные классы служат базой для других классов. Account содержит универсальные свойства для всех банковских счетов. Вы можете создать другие типы банковских счетов, наследуя класс Account, но сам этот класс не может быть реализован.
Создание полноценного класса из абстрактного...255
Подкласс абстрактного класса остаётся абстрактным, пока в нём не переопределены все чисто виртуальные функции. Класс Savings не является абстрактным, поскольку переопределяет чисто виртуальную функцию withdrawal( ) совершенно реальной. Объект класса Savings отлично знает, как реализовать функцию withdrawal( ) и куда обращаться при её вызове. То же касается и класса Checking: он не виртуальный, поскольку withdrawal( ) переопределяет чисто виртуальную функцию, определённую ранее в базовом классе.
_________________
255 стр. Глава 22. Разложение классов
Подкласс абстрактного класса, конечно, может оставаться абстрактным. Разберёмся с приведёнными ниже классами.
class Display
{
public :
virtual void initialize( ) = 0 ;
virtual void write( char *pString ) = 0 ;
} ;
class SVGA : public Display
{
/* Сделаем обе функции-члена "реальными" */
virtual void initialize( ) ;
virtual void write( char *pString ) ;
} ;
class HWVGA : public Display
{
/* Переопределим только одну функцию */
virtual void write( char *pString ) ;
} ;
class ThreedVGA : public HWVGA
{
virtual void initialize( ) ;
} ;
void fn( )
{
SVGA mc ;
VGA vga ;
/* Всё остальное */
}
Класс Display, описывающий дисплеи персонального компьютера, содержит две чисто виртуальные функции: initialize( ) и write( ). Вы не можете ввести эти функции в общем виде. Разные типы видеоадаптеров инициализируются и осуществляют вывод по-разному.
Один из подклассов — SVGA — не абстрактный. Это отдельный тип видеоадаптера, и программист точно знает, как его реализовать. Таким образом, класс SVGA переопределяет обе функции — initialize( ) и write( ) — именно так, как необходимо для данного адаптера.
Ещё один подкласс — HWVGA . Программисту известно, как программировать ускоренный VGA-адаптер. Поэтому между общим классом Display и его частным случаем, ThreedVGA, который представляет собой специальный тип карт 3-D, находится ещё один уровень абстракции.
В нашем обсуждении предположим, что запись во все аппаратно ускоренные карты VGA происходит одинаково ( это не соответствует истине, но представим себе, что это так ). Чтобы правильно выразить общее свойство записи, вводится класс HWVGA, реализующий функцию write( ) ( и другие общие для HWVGA свойства ).
_________________
256 стр. Часть 4. Наследование
При этом функция initialize( ) не переопределяется, поскольку для разных типов карт HWVGA она реализуется по-разному.
Несмотря на то что функция write( ) переопределена в классе HWVGA, он всё равно остаётся абстрактным, так как функция initialize( ) всё ещё не переопределена.