Освой самостоятельно С++ за 21 день.
Шрифт:
Действительно, если вы решите использовать в своей программе множественное наследование, следует учесть, что с отладкой программы могут возникнуть проблемы и чрезмерное усложнение программы, связанное с использованием этого подхода, не всегда оправдывается полученным эффектом.
Указание виртуального наследования при объявлении класса
Чтобы быть уверенным, что производные классы будут рассматривать исходный базовый класс как единый источник, виртуальность наследования
Пример 1:
classHorse : virtual public Animal class Bird : virtual public Animal '. class Pegasus: public Horse,public Bird
Пример 2:
class Schnauzer : virtual public 0og class Poodle ; virtual public 0og class Schnoodle : public Schnauzer, publiс Poodle
Рекомендуется:Используйте множественное наследование в тех случаях, когда в классе необходимо применять данные и методы, объявленные в разных классах. Используйте виртуальное наследование, чтобы как можно элегантнее обойти проблемы с неопределенностью источника наследования метода или данных. Инициализируйте исходный базовый класс конструктором класса, наиболее удаленного от базового по иерархии классов.
Не рекоменддется:Не используйте множественное наследование в тех случаях, когда можно обойтись одиночным наследованием.
Классы-мандаты
Промежуточным решением между одиночным и множественным наследованием классов может быть использование классов-мандатов. Так, класс Horse можно произвести от двух базовых классов — Animal и Displayable, причем последний добавляет только некоторые методы отображения объектов на экране.
Классом-мандатом называется класс, открывающий доступ к ряду методов, но не содержащий никаких данных (или, по крайней мере, содержащий минимальный набор данных).
Методы класса-мандата передаются в производные классы с помощью обычного наследования. Единственное отличие классов-мандатов от других классов состоит в том, что они практически не содержат никаких данных. Различие довольно субъективное и отражает только общую тенденцию программирования, сводящуюся к тому, что добавление функциональности классам не должно сопровождаться усложнением программы. Использование классов-мандатов также снижает вероятность возникновения неопределенностей при использовании в производном классе данных, унаследованных из других базовых классов.
Например, предположим, что класс Horse производится от двух классов — Animal и Displayable, причем последний добавляет только новые методы, но не содержит данных. В таком случае все наследуемые данные класса Horse происходят только от одного базового класса Animal, а методы наследуются от обоих классов.
Классы-мандаты (capability class) иногда еще называют миксинами (mixin). Этот термин произошел от названия десерта, представляющего собой смесь пирожного с мороженым, политую сверху шоколадной глазурью. Этот десерт продавался в супермаркетах Sommerville в штате Массачусетс. Видимо, это блюдо когда-то попробовал один из программистов, занимающийся разработкой средств объектно-ориентированного программирования для языка SCOOPS, где этот термин впервые появился.
Абстрактные типы данных
В объектном программировании довольно часто создаются иерархии логически связанных классов. Например, представим класс Shape, от которого произведены классы Rectangle и Circle. Затем от класса Rectangle производится класс Sguare, как частный вид прямоугольника.
В каждом из производных классов замещаются методы Draw, GetArea и др. Основной костяк программы с классом Shape и производными от него Rectangle и Circle показан в листинге 13.7.
Листинг 13.7. Классы семейства Shape
1: // Листинг 13.7. Классы семейства Shape
2:
3: #include <iostream.h>
4:
5:
6: class Shape
7: {
8: public:
9: Shape{ }
10: virtual ~Shape { }
11: virtual long GetArea { return -1; }
12: virtual long GetPerim { return -1; }
13: virtual void Draw { }
14: private:
15: };
16:
17: class Circle : public Shape
18: {
19: public:
20: Circle(int radius):itsRadius(radius) { }
21: ~Circle { }
22: long GetArea { return 3 * itsRadius * itsRadius; }
23: long GetPerim { return 6 * itsRadius; }
24: void Draw;
25: private:
26: int itsRadius;
27: int itsCircumference;
28: };
29:
30: void Circle::Draw
31: {
32: cout << "Circle drawing routine here!\n";
33: }
34:
35:
36: class Rectangle : public Shape
37: {
38: public:
39: Rectangle(int len, int width);
40: itsLength(len), itsWidth(width) { }
41: virtual ~Rectangle { }
42: virtual long GetArea { return itsLength * itsWidth; }
43: virtual long GetPerim { return 2*itsLength + 2*itsWidth; }
44: virtual int GetLength { return itsLength; }
45: virtual int GetWidth { return itsWidth; }
46: virtual void Draw;
47: private:
48: int itsWidth;
49: int itsLength;
50: };
51:
52: void Rectangle::Draw
53: {
54: for (int i = 0; i<itsLength; i++)
55: {
56: for (int j = 0; j<itsWidth; j++)
57: cout << "x ";
58:
59: cout << "\n";
60: }
61: }
62:
63: class Square : public Rectangle
64: {
65: public: