Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Добавление объекта в два списка



 

Другая проблема состоит в том, что при объявлении Pegasus как объекта типа Horse становится невозможным добавить его в список объектов класса Bi rd. Приходилось то переносить функцию Fly() вверх по иерархии классов, то выполнять приведение указателя, но так и не удалось в полной мере достичь необходимого функционирования программы.

Таким образом, придерживаясь только одиночного наследования, оказалось невозможным элегантно решить эту проблему. Можно перенести все три функции — Fly(), Whinny() и Gallop() — в базовый класс Animal, общий для двух производных классов Bird и Horse. В результате вместо двух списков объектов для классов Bird и Horse получится один общий список объектов класса Animal. Недостаток метода состоит в том, что базовый класс принимает на себя слишком много функций.

В качестве альтернативы можно оставить методы там, где они есть, и заняться приведением типов объектов классов Horse, Bird и Pegasus, но результат в конечном итоге будет еще хуже!

 

Рекомендуется: Переносите вверх по иерархии классов функции общего использования. Избегайте использовать коды, основанные на определении типов объектов во время выполнения программы. Вместо этого используйте виртуальные методы, шаблоны и множественное наследование.

 

Не рекомендуется: Не переносите вверх по иерархии классов интерфейсы производных классов.

 

 

Множественное наследование

 

Существует возможность производить новые классы более чем от одного базового класса. Такой процесс называется множественным наследованием. Чтобы произвести подобный класс, базовые классы в объявлении разделяются запятыми. В листинге 13.3 класс Pegasus объявлен таким образом, что наследует свойства двух базовых классов — Bird и Horse. Затем программа добавляет объект Pegasus в списки объектов обоих классов.

Листинг 13.3. Множественное наследование

1: // Листинг 13.3. Множественное наследование.

2: // Множественное наследование

3:

4: #include < iostream.h>

5:

6: class Horse

7: {

8: public:

9: Horse() { cout < < " Horse constructor... "; }

10: virtual ~Horse() { cout < < " Horse destructor... "; }

11: virtual void Whinny() const { cout < < " Whinny!... "; }

12: private:

13: int itsAge;

14: };

15:

16: class Bird

17: {

18: public:

19: Bird() { cout < < " Bird constructor... "; }

20: virtual ~Bird() { cout < < " Bird destructor... "; }

21: virtual void Chirp() const { cout < < " Chirp... "; }

22: virtual void Fly() const

23: {

24: cout < < " I can fly! I can fly! I can fly! ";

25: }

26: private:

27: int itsWeight;

28: };

29:

30: class Pegasus: public Horse, public Bird

31: {

32: public:

33: void Chirp() const { Whinny(); }

34: Pegasus() { cout < < " Pegasus constructor... "; }

35: ~Pegasus() { cout < < " Pegasus destructor... "; }

36: };

37:

38: const int MagicNumber = 2;

39: int main()

40: {

41: Horse* Ranch[MagicNumber];

42: Bird* Aviary[MagicNumber];

43: Horse * pHorse;

44: Bird * pBird;

45: int choice, i;

46: for (i=0; i< MagicNumber; i++)

47: {

48: cout < < " \n(1)Horse (2)Pegasus: ";

49: cin > > choice;

50: if (choice == 2)

51: pHorse = new Pegasus;

52: else

53: pHorse = new Horse;

54: Ranch[i] = pHorse;

55: }

56: for (i=0; i< MagicNumber; i++)

57: {

58: cout < < " \n(1)Bird (2)Pegasus: ";

59: cin > > choice;

60: if (choice == 2)

61: pBird = new Pegasus;

62: else

63: pBird = new Bird;

64: Aviary[i] = pBird;

65: }

66:

67: cout < < " \n";

68: for (i=0; i< MagicNumber; i++)

69: {

70: cout < < " \nRanch[" < < i < < " ]:

71: Ranch[i]-> Whinny();

72: delete Ranch[i];

73: }

74:

75: for (i=0; i< MagicNumber; i++)

76: {

77: cout < < " \nAviary[" < < i < < " ]

78: Aviary[i]-> Chirp();

79: Aviary[i]-> Fly();

80: delete Aviary[i];

81: }

82: return 0;

83: }

 

Результат:

(1)Horse (2)Pegasus: 1

Horse constructor...

(1)Horse (2)Pegasus: 2

Horse constructor... Bird constructor... Pegasus constructor...

(1)Bird (2)Pegasus: 1

Bird constructor...

(1)6ird (2)Pegasus: 2

Horse constructor... Bird constructor... Pegasus constructor...

Ranch[0]: Whinny!... Horse destructor...

Ranch[1]: Whinny!... Pegasus destructor... Bird destructor...

Horse destructor...

Aviary[0]: Chirp... I can fly! I can fly! I can fly! Bird destructor...

Aviary[1]: Whinny!... I can fly! I can fly! I can fly!

Pegasus destructor... Bird destructor... Horse destructor...

 

Анализ: В строках 6—14 объявляется класс Horse. Конструктор и деструктор выводят на экран сообщения о своей работе, а метод Whinny() печатает Whinny! (И-го-го).

Класс Bird объявляется в строках 16—28. В дополнение к своим конструктору и деструктору этот класс содержит два метода: Chirp() и Fly(), каждый из которых выводит на экран соответствующие сообщения. В реальных программах эти методы могут воспроизводить определенный звуковой файл или управлять анимационными эффектами на экране.

Наконец, в строках 30-36 объявляется класс Pegasus. Он производится сразу от двух базовых классов — Bird и Horse. В классе замешается метод Chirp() таким образом, что вызывается метод Whinny(), который унаследован этим классом от класса Horse.

Создается два списка: Ranch (конюшня), который в строке 41 связывается с классом Horse, и Aviary (птичник), который в строке 42 связывается с классом Bird. В строках 46—55 в список Ranch добавляются два объекта — Horse и Pegasus. В строках 56—65 в список Aviary добавляются объекты Bird и Pegasus.

Вызовы виртуальных методов с помощью указателей классов Bird и Horse одинаково выполняются для объекта Pegasus. Например, в строке 78 метод Chirp() вызывается последовательно для всех объектов, указатели на которые представлены в массиве Aviary. Поскольку этот метод объявлен в классе Bird как виртуальный, он правильно Выполняется для всех объектов списка.

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

 

Объявление множественного наследования

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

Пример 1:

class Pegasus: public Horse, public Bird

Пример 2:

class Schnoodle: public Schnauzer, public Poodle

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

 

Когда в памяти компьютера создается объект Pegasus, конструкторы обоих классов принимают участие в его построении, как показано на рис. 13.1.

 

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

 

В случае использования множественного наследования возникает ряд непростых и весьма интересных вопросов. Например, что произойдет, если оба базовых класса будут иметь одно и то же имя либо содержать виртуальные функции или данные с одинаковыми именами? Как инициализируются конструкторы разных базовых классов? Что произойдет, если два базовых класса будут произведены от одного и того же родительского класса? Все эти вопросы будут рассмотрены в следующем разделе, после чего можно переходить к практическому использованию множественного наследования.

 

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

 

Если класс Pegasus производится от двух базовых классов — Bird и Horse, а в каждом из них объявлены конструкторы со списками параметров, то класс Pegasus инициализирует эти конструкторы. Как это происходит, показано в листинге 13.4.

Листинг 13.4. Создание объектов при множественном наследовании

1: // Листинг 13.4.

2: // Создание обьектов при множественном наследовании

3: #include < iostream.h>

4: typedef int HANDS;

5: enum COLOR { Red, Green, Blue, Yellow, White, Black, Brown };

6:

7: class Horse

8: {

9: public:

10: Horse(COLOR color, HANDS height);

11: virtual ~Horse() { cout < < " Horse destructor...\n"; }

12: virtual void Whinny()const { cout < < " Whinny!... "; }

13: virtual HANDS GetHeight() const { return itsHeight; }

14: virtual COLOR GetColor() const { return itsColor; }

15: private:

16: HANDS itsHeight;

17: COLOR itsColor;

18: };

19:

20: Horse:: Horse(COLOR color, HANDSheight):

21: itsColor(color), itsHeight(height)

22: {

23: cout < < " Horse constructor...\n";

24: }

25:

26: class Bird

27: {

28: public:

29: Bird(COLOR color, bool migrates);

30: virtual ~Bird() { cout < < " Bird destructor...\n"; }

31: virtual void Chirp()const { cout < < " Chirp... "; }

32: virtual void Fly()const

33: {

34: cout < < " I can fly! I can fly! I can fly! ";

35: }

36: virtual COLOR GetColor()const { return itsColor; }

37: virtual bool GetMigration() const { return itsMigration; }

38:

39: private:

40: COLOR itsColor;

41: bool itsMigration;

42: };

43:

44: Bird:: Bird(COLOR color, bool migrates):

45: itsColor(color), itsMigration(migrates)

46: {

47: cout < < " Bird constructor...\n";

48: }

49:

50: class Pegasus: public Horse, public Bird

51: {

52: public:

53: void Chirp()const { Whinny(); }

54: Pegasus(COLOR, HANDS, bool, long);

55: ~Pegasus() { cout < < " Pegasus destructor...\n"; }

56: virtual long GetNumberBelievers() const

57: {

58: return itsNumberBelievers;

59: }

60:

61: private:

62: long itsNumberBelievers;

63: };

64:

65: Pegasus:: Pegasus(

66: COLOR aColor,

67: HANDS height,

68: bool migrates,

69: long NumBelieve):

70: Horse(aColor, height),

71: Bird(aColor, migrates),

72: itsNumberBelievers(NumBelieve)

73: {

74: cout < < " Pegasus constructor...\n";

75: }

76:

77: int main()

78: {

79: Pegasus *pPeg = new Pegasus(Red, 5, true, 10);

80: pPeg-> Fly();

81: pPeg-> Whinny();

82: cout < < " \nYour Pegasus is " < < pPeg-> GetHeight();

83: cout < < " hands tall and ";

84: if (pPeg-> GetMigration())

85: cout < < " it does migrate.";

86: else

87: cout < < " it does not migrate.";

88: cout < < " \nA total of " < < pPeg-> GetNumberBelievers();

89: cout < < " people believe it exists.\n";

90: delete pPeg;

91: return 0;

92: }

 

Результат:

Horse constructor...

Bird constructor...

Pegasus constructor...

I can fly! I can fly! I can fly! Whinny!...

Your Pegasus is 5 hands tall and it does migrate.

A total of 10 people believe it exists.

Pegasus destructor...

Bird destructor...

Horse destructor...

 

Анализ: Класс Horse объявляется в строках 7—18. Конструктор этого класса принимает два параметра: один из них — это перечисление, объявленное в строке 5, а второй — новый тип, объявленный с помощью typedef в строке 4. Этот конструктор выполняется в строках 20—24. При этом инициализируется одна переменная-член и на экран выводится сообщение о работе конструктора класса Horse.

В строках 26—42 объявляется класс Bird, конструктор которого выполняется в строках 45—49. Конструктор этого класса также принимает два параметра. Обратите внимание на интересный факт: конструкторы обоих классов принимают перечисления цветов, с помощью которых в программе можно установить цвет лошади или цвет перьев у птицы. В результате, когда вы попытаетесь установить цвет Пегаса, может возникнуть проблема в работе программы, которая обсуждается несколько ниже.

Класс Pegasus объявляется в строках 50—63, а его конструктор — в строках 65—75. Инициализация объекта Pegasus выполняется тремя строками программы. Сначала конструктор класса Horse определяет цвет и рост. Затем конструктор класса Bird инициализируется цветом перьев и логической переменной. Наконец, происходит инициализация переменной-члена itsNumberBelievers, относящейся к классу Pegasus. После всех этих операций вызывается конструктор класса Pegasus.

В функции main() создается указатель на класс Pegasus, который используется для получения доступа к функциям-членам базовых объектов.

 

Двусмысленность ситуации

 

В листинге 13.4 оба класса — Horse и Bird — имеют метод GetColor(). В программе может потребоваться возвратить цвет объекта Pegasus, но возникает вопрос: какой из двух унаследованных методов при этом будет использоваться? Ведь методы, объявленные в обоих базовых классах, имеют одинаковые имена и сигнатуры. В результате при компилировании программы возникнет неопределенность, которую необходимо разрешить до компиляции.

Если просто записать:

COLOR currentColor = pPeg-> GetColor();

Компилятор покажет сообщение об ошибке Member is ambiguous: ' Horse:: GetColor' and ' Bird:: GetColor' (Член не определен).

Эту неопределенность можно разрешить, явно обратившись к методу того класса, который вам необходим:

COLOR currentColor = pPeg-> Horse:: GetColor();

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

Если в классе Pegasus эта функция будет замещена, то проблема решится сама собой, так как в этом случае вызывается функция-член класса Pegasus:

virtual COLOR GetColor()const { return Horse:: GetColor(); }

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

COLOR currentColor = pPeg-> Bird:: GetColor();

 


Поделиться:



Популярное:

Последнее изменение этой страницы: 2017-03-08; Просмотров: 651; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.058 с.)
Главная | Случайная страница | Обратная связь