Главная->Інформатика та програмування->Содержание->Абстрактные классы и чистые виртуальные функции

ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В C++ (4-Е ИЗДАНИЕ) (часть 11) онлайн

Абстрактные классы и чистые виртуальные функции

Давайте вернемся к классу shape из программы MULTSHAP (глава 9). Мы никогда

не станем создавать объект из класса shape, разве что начертим несколько гео-

метрических фигур — кругов, треугольников и т. п. Базовый класс, объекты ко-

торого никогда не будут реализованы, называется абстрактным классом. Такой

класс может существовать с единственной целью — быть родительским по отно-

шению к производным классам, объекты которых будут реализованы. Еще он

может служить звеном для создания иерархической структуры классов.

Как нам объяснить людям, использующим созданную нами структуру клас-

сов, что объекты родительского класса не предназначены для реализации? Мож-

но, конечно, заявить об этом в документации, но это никак не защитит наш базо-

зый класс от использования не по назначению. Надо защитить его программно.

Для этого достаточно ввести в класс хотя бы одну чистую виртуальную функ-

цию. Чистая виртуальная функция — это функция, после объявления которой

добавлено выражение =0. Продемонстрируем сказанное в примере VIRTPURE:

Листинг 11.3. Программа VIRTPURE

// virtpure.cpp

// Чистая виртуальная функция

  #include <iostream>

  using namespace std;

  class Base                   //базовый класс

    {

    public:

      virtual void show() = 0; //чистая виртуальная функция

    };

  //////////////////////////////////////////////////

 

Листинг 11.3 (продолжение)

 

   class Derv1 : public Base    //порожденный класс 1

    {

    public:

      void show() 

        { cout << "Derv1\n"; }

    };

  //////////////////////////////////////////////////

  class Derv2 : public Base    //порожденный класс 2

    {

    public:

      void show()

        { cout << "Derv2\n"; }

    };

  /////////////////////////////////////////////////

  int main()

  {

    // Base bad;               //невозможно создать объект

                               //из абстрактного класса

    Base* arr[2];              //массив указателей на

                               //базовый класс

    Derv1 dv1;                 //Объект производного класса 1

    Derv2 dv2;                 //Объект производного класса 2

   

    arr[0] = &dv1;             //Занести адрес dv1 в массив

    arr[1] = &dv2;             //Занести адрес dv2 в массив

 

    arr[0]->show();            //Выполнить функцию show()

    arr[1]->show();            //над обоими объектами

    return 0;

  }

 

Здесь виртуальная функция show() объявляется так:

 

virtual void show() = 0; //чистая виртуальная функция

Знак равенства не имеет ничего общего с операцией присваивания. Нулевое

значение ничему не присваивается. Конструкция =0 — это просто способ сооб-

щить компилятору, что функция будет чистой виртуальной. Если теперь в main()

попытаться создать объект класса Base, то компилятор будет недоволен тем, что

объект абстрактного класса пытаются реализовать. Он выдаст даже имя чистой

виртуальной функции, которая делает класс абстрактным. Помните, что хотя

это только объявление, но определение функции show() базового класса не явля-

ется обязательным. Впрочем, если вам надо его написать, это можно сделать.

Как только в базовом классе окажется чистая виртуальная функция, необхо-

димо будет позаботиться о том, чтобы избежать ее употребления во всех произ-

водных классах, которые вы собираетесь реализовать. Если класс использует

чистую виртуальную функцию, он сам становится абстрактным, никакие объекты

 

из него реализовать не удастся (производные от него классы, впрочем, уже не

имеют этого ограничения). Более из эстетических соображений, нежели из каких-

либо иных, можно все виртуальные функции базового класса сделать чистыми.

Между прочим, мы внесли еще одно, не связанное с предыдущими, измене-

ние в VIRTPURE: теперь адреса методов хранятся в массиве указателей и доступ-

ны как элементы этого массива. Обработка этого, впрочем, ничем не отличается

от использования единственного указателя. VIRTPURE выдает результат, не отли-

чающийся от VIRT:

Derv1

Derv2

 

7