Вход/Регистрация
Программирование. Принципы и практика использования C++ Исправленное издание
вернуться

Страуструп Бьерн

Шрифт:

В разделе 14.2.1 мы видели, как создать абстрактный класс, объявив его конструктор в разделе protected. Существует другой — более распространенный — способ создания абстрактного класса: указать, что одна или несколько его виртуальных функций будет замещена в производном классе. Рассмотрим пример.

class B { // абстрактный базовый класс

public:

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

virtual void g=0;

};

B b; // ошибка: класс B — абстрактный

Интересное обозначение

=0
указывает на то, что виртуальные функции
B::f
и
B::g
являются чистыми, т.е. они должны быть замещены в каком-то производном классе. Поскольку класс B содержит чисто виртуальную функцию, мы не можем создать объект этого класса. Замещение чисто виртуальных функций устраняет эту проблему.

class D1:public B {

public:

void f;

void g;

};

D1 d1; // OK

Несмотря на то что все чисто виртуальные функции замещаются, результирующий класс остается абстрактным.

class D2:public B {

public:

void f;

// no g

};

D2 d2; // ошибка: класс D2 — (по-прежнему) абстрактный

class D3:public D2 {

public:

void g;

};

D3 d3; // OK

Классы с чисто виртуальными функциями обычно описывают исключительно интерфейс; иначе говоря, они, как правило, не содержат данных-членов (эти данные хранятся в производных классах) и, следовательно, не имеют конструкторов (если инициализация данных-членов не нужна, то необходимость в конструкторах отпадает).

14.4. Преимущества объектно– ориентированного программирования

Когда мы говорим, что класс
Circle
является производным от класса
Shape
, или разновидностью класса Shape, то делаем это для того, чтобы достичь следующих целей (по отдельности или всех вместе).

• Наследование интерфейса. Функция, ожидающая аргумент класса

Shape
(обычно в качестве аргумента, передаваемого по ссылке), может принять аргумент класса
Circle
(и использовать его с помощью интерфейса класса
Shape
).

• Наследование реализации. Когда мы определяем класс

Circle
и его функции-члены, мы можем использовать возможности (т.е. данные и функции-члены), предоставляемые классом
Shape
.

Проект, в котором не используется наследование интерфейса (т.е. проект, в котором объект производного класса нельзя использовать вместо объекта открытого базового класса), следует признать плохим и уязвимым для ошибок. Например, мы могли бы определить класс
Never_do_this
, относительно которого класс
Shape
является открытым базовым классом. Затем мы могли бы заместить функцию
Shape::draw_lines
функцией, которая не рисует фигуру, а просто перемещает ее центр на 100 пикселей влево. Этот проект фатально неверен, поскольку, несмотря на то, что класс
Never_do_this
может предоставить интерфейс класса
Shape
, его реализация не поддерживает семантику (т.е. поведение), требуемое классом
Shape
. Никогда так не делайте!

Преимущества наследования интерфейса проявляются в использовании интерфейса базового класса (в данном случае класса
Shape
) без информации о реализациях (в данном случае классах, производных от класса
Shape
).

Преимущества наследования интерфейса проявляются в упрощении реализации производных классов (например, класса
Circle
), которое обеспечивается возможностями базового класса (например, класса
Shape
).

Обратите внимание на то, что наш графический проект сильно зависит от наследования интерфейса: “графический движок” вызывает функцию
Shape::draw
, которая в свою очередь вызывает виртуальную функцию
draw_lines
класса
Shape
, чтобы она выполнила реальную работу, связанную с выводом изображений на экран. Ни “графический движок”, ни класс
Shape
не знают, какие виды фигур существуют. В частности, наш “графический движок” (библиотека FLTK и графические средства операционной системы) написан и скомпилирован за много лет до создания наших графических классов! Мы просто определяем конкретные фигуры и вызываем функцию
attach
, чтобы связать их с объектами класса
Window
в качестве объектов класса
Shape
(функция
Window::attach
получает аргумент типа
Shape&
; см. раздел Г.3). Более того, поскольку класс
Shape
не знает о наших графических классах, нам не нужно перекомпилировать класс
Shape
каждый раз, когда мы хотим определить новый класс графического интерфейса.

  • Читать дальше
  • 1
  • ...
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: