Чтение онлайн

на главную

Жанры

Программирование. Принципы и практика использования 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
каждый раз, когда мы хотим определить новый класс графического интерфейса.

Иначе говоря, мы можем добавлять новые фигуры, не модифицируя существующий код. Это “святой Грааль” для проектирования, разработки и сопровождения программного обеспечения: расширение системы без ее модификации. Разумеется, существуют пределы, до которых мы можем расширять систему, не модифицируя существующие классы (например, класс
Shape
предусматривает довольно ограниченный набор операций), и этот метод не может решить все проблемы программирования (например, в главах 17–19 определяется класс
vector
; наследование здесь мало может помочь). Однако наследование интерфейса — один из мощных методов проектирования и реализации систем, устойчивых к изменениям.

Аналогично наследование реализации позволяет сделать многое, но тоже не является панацеей. Помещая полезные функции в класс
Shape
, мы экономим силы, избегая дублирования кода в производных классах. Это может оказаться существенным фактором при разработке реальных программ. Однако этот эффект достигается за счет того, что любое изменение интерфейса класса
Shape
или любое изменение в размещении его данных-членов потребует повторной компиляции всех производных классов и их клиентов. Для широко используемых библиотек такая повторная компиляция может оказаться неразрешимой проблемой. Естественно, существуют способы достичь указанных преимуществ и избежать большинства проблем (см. раздел 14.3.5).

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

Право налево

Зика Натаэль
Любовные романы:
современные любовные романы
8.38
рейтинг книги
Право налево

Третий. Том 2

INDIGO
2. Отпуск
Фантастика:
космическая фантастика
попаданцы
5.00
рейтинг книги
Третий. Том 2

Гром над Империей. Часть 2

Машуков Тимур
6. Гром над миром
Фантастика:
фэнтези
попаданцы
5.25
рейтинг книги
Гром над Империей. Часть 2

Сама себе хозяйка

Красовская Марианна
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Сама себе хозяйка

Бальмануг. Невеста

Лашина Полина
5. Мир Десяти
Фантастика:
юмористическое фэнтези
5.00
рейтинг книги
Бальмануг. Невеста

Аромат невинности

Вудворт Франциска
Любовные романы:
любовно-фантастические романы
эро литература
9.23
рейтинг книги
Аромат невинности

Чемпион

Демиров Леонид
3. Мания крафта
Фантастика:
фэнтези
рпг
5.38
рейтинг книги
Чемпион

Эксперимент

Юнина Наталья
Любовные романы:
современные любовные романы
4.00
рейтинг книги
Эксперимент

Небо для Беса

Рам Янка
3. Самбисты
Любовные романы:
современные любовные романы
5.25
рейтинг книги
Небо для Беса

Идеальный мир для Социопата 7

Сапфир Олег
7. Социопат
Фантастика:
боевая фантастика
6.22
рейтинг книги
Идеальный мир для Социопата 7

Я не князь. Книга XIII

Дрейк Сириус
13. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я не князь. Книга XIII

Камень. Книга пятая

Минин Станислав
5. Камень
Фантастика:
боевая фантастика
6.43
рейтинг книги
Камень. Книга пятая

Романов. Том 1 и Том 2

Кощеев Владимир
1. Романов
Фантастика:
фэнтези
попаданцы
альтернативная история
5.25
рейтинг книги
Романов. Том 1 и Том 2

Ваше Сиятельство 4т

Моури Эрли
4. Ваше Сиятельство
Любовные романы:
эро литература
5.00
рейтинг книги
Ваше Сиятельство 4т