Выбрать главу

        void deposit( ) ;

        /* Приведённая ниже функция является чисто виртуальной */

        virtual void withdrawal( float amount ) = 0 ;

    protected :

        /* Если хранить счета в связанном списке, не будет ограничения на их количество */

        static Account *pFirst ;

        Account *pNext ;

        static int count ; /* Количество счетов */

        unsigned accountNumber ;

        float balance ;

    } ;

______________

17Вообще говоря, чисто виртуальная функция может иметь тело, но обсуждение этого вопроса выходит за рамки данной книги. — Прим. ред.

_________________

254 стр. Часть 4. Наследование

Наличие после объявления функции withdrawal( ) символов = 0 показывает, что программист не намеревается в данный момент определять эту функцию. Такое объявление просто занимает место для тела функции, которое позже будет реализовано в подклассах. От подклассов класса Account ожидается, что они переопределят эту функцию более конкретно.

«Я считаю это объяснение глупым, и мне оно нравится не более чем вам, так что просто выучите и живите с ним. Для этого объяснения есть причина, если не оправдание. Каждая виртуальная функция должна иметь свою ячейку в специальной таблице, в которой содержится адрес функции. Так вот: ячейка для чисто виртуальной функции содержит нуль.»

[Технические подробности]

Абстрактный класс не может быть реализован; другими словами, вы не можете создать объект абстрактного класса. Например, приведённое ниже объявление некорректно.

    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( ). Вы не можете ввести эти функции в общем виде. Разные типы видеоадаптеров инициализируются и осуществляют вывод по-разному.