Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
###Cpp_лкц1_1.09_11_#дляБАК#29_01_12.doc
Скачиваний:
39
Добавлен:
29.04.2019
Размер:
6.42 Mб
Скачать

Глава 9. Рекомендации по программированию

245

// Конструктор копирования: Y(const Y & г): Х(г). s(r.s){} }: Операция присваивания не наследуется, поэтому она должна быть определена в производных классах. При этом из нее следует явным образом вызывать соответствующую операцию базового класса (см. с. 204).

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

Открытое наследование класса Y из класса X означает, что Y представляет собой разновидность класса X, то есть более конкретную, частную концепцию. Базовый класс X является более общим понятием, чем Y1. Везде, где можно использовать X, можно использовать и Y, но не наоборот (вспомните, что на место ссылок на базовый класс можно передавать ссылку на любой из производных). Необходимо помнить, что во время выполнения программы не существует иерархии классов и передачи сообщений объектам базового класса из производных — есть только конкретные объекты классов, поля которых формируются на основе иерархии на этапе компиляции.

Методы, которые должны иметь все производные классы, но которые не могут быть реализованы на уровне базового класса, должны быть виртуальными. Например, все объекты иерархии должны уметь выводить информацию о себе. Поскольку она хранится в различных полях производных классов, эту функцию нельзя реализовать в базовом классе. Естественно назвать ее во всех классах одинаково и объявить как виртуальную с тем, чтобы другие методы базового класса могли вызывать ее в зависимости от фактического типа объекта, с которым они работают. По этой причине деструкторы объявляются как виртуальные.

При переопределении виртуальных методов нельзя изменять наследуемое значение аргумента по умолчанию, поскольку по правилам C++ оно определяется типом указателя, а не фактическим типом объекта, вызвавшего метод:

#include <iostream.h> class X{ public:

virtual void fundnt a = 0){cout « a:}

}:

class Y: public X{ public:

virtual void fun(int a = 1) {cout «a:}

}:

int main(){

X *px = new X:

px->fun(): // Выводится О

1 Например, каждый программист — человек, но не каждый человек — программист.

246

Часть II. Объектно-ориентированное программирование

X *ру = new Y;

py->fun(); // Выводится О

} Невиртуальные методы переопределять в производных классах не рекомендуется, поскольку производные классы должны наследовать свойства базовых. Иными словами, невиртуальный метод инвариантен относительно специализации, то есть сохраняет те свойства, которые должны наследоваться из базового класса независимо от того, как конкретизируется (специализируется) производный класс. Специализация производного класса достигается добавлением новых методов и переопределением существующих виртуальных методов.

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

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

class X{ ....}; class Y{ X х;

}: Вложение представляет отношения классов «Y содержит X» или «Y реализуется посредством X». Необходимость использовать вложение вместо наследования можно определить, задав себе вопрос, может ли у Y быть несколько объектов класса X. Если требуется, к примеру, описать класс для моделирования самолета, будет логично описать в нем поле типа «двигатель»: самолет содержит двигатель, но не является его разновидностью.

Отношение «реализуется посредством» используется вместо наследования тогда, когда про классы X и Y нельзя сказать, что Y является разновидностью X, но при этом Y использует часть функциональности X. Следует предпочитать вложение наследованию.

В случаях, когда между классами нет логической взаимосвязи, а требуется просто использовать часть кода одного класса в другом, может быть полезным использовать закрытое наследование (с ключом доступа private). Этот способ используется, когда в производном классе требуется доступ к защищенным элементам базового класса и замещение его виртуальных методов.

Шаблоны классов используются для создания семейств классов, поведение которых не зависит от типа объектов. Шаблоны следует использовать аккуратно, отдавая себе отчет в том, что для каждого типа порождается собственная копия шаблона, что может привести к разбуханию кода. Для уменьшения размера кода рекомендуется вынести в базовый класс все методы, не использующие информацию о типе объекта, и унаследовать от него шаблонный класс со всеми остальными методами. Это позволит избежать дублирования тех методов, тексты которых не зависят от параметра шаблона.