Рейтинговые книги
Читем онлайн Освой самостоятельно С++ за 21 день. - Джесс Либерти

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 81 82 83 84 85 86 87 88 89 ... 170

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();

Наследование от общего базового класса

Что произойдет, если оба базовых класса, от которых производится другой класс, сами были произведены от одного общего базового класса, как, например, классы Bird и Horse от класса Animal. Эта ситуация показана на рис. 13.2.

Рис. 13.2. Общий базовый класс

Как показано на рис. 13.2, два класса, являющихся базовыми для класса Pegasus, сами производятся от одного общего класса Animal. Компилятор при этом рассматривает классы Bird и Horse как производные от двух одноименных базовых классов, что

может привести к очередной неопределенности. Например, если в классе Animal объявлены переменная-член itsAge и функция-член GetAge(), а в программе делается вызов pGet->GetAge(), то будет ли при этом вызываться функция GetAge(), унаследованная классом Bird от класса Animal или классом Horse от базового класса? Это противоречие разрешается в листинге 13.5.

Листинг 13.5. Общий базовый класс

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

2: // Общий базовый класс

3: #include <iostream.h>

4:

5: typedef int HANDS;

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

7:

8: class Animal // общий базовый класс для классов horse и bird

9: {

10:   public:

11:      Animal(int);

12:      virtual ~Animal() { cout << "Animal destructor...n"; }

13:      virtual int GetAge() const { return itsAge; }

14:      virtual void SetAge(int age) { itsAge = age; }

15:   private:

16:      int itsAge;

17: };

18:

19: Animal::Animal(int age):

20: itsAge(age)

21: {

22:    cout << "Animal constructor...n";

23: }

24:

25: class Horse : public Animal

26: {

27:    public:

28:       Horse(COLOR color, HANDS height, int age);

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

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

31:       virtual HANOS GetHeight() const { return itsHeight; }

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

33:    protected:

34:       HANDS itsHeight;

35:       COLOR itsColor;

36: };

37:

38: Horse::Horse(C0L0R color, HANDS height, int age):

39:    Animal(age),

40:    itsColor(color),itsHeight(height)

41: {

42:    cout << "Horse constructor...n";

43: }

44:

45: class Bird : public Animal

46: {

47:    public:

48:       Bird(COLOR color, bool migrates, int age);

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

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

51:       virtual void Fly()const

52:          { cout << "I can fly! I can fly! I can fly! "; }

53:       virtual C0L0R GetColor()const { return itsColor; }

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

55:    protected:

56:       COLOR itsColor;

57:       bool itsMigration;

58: };

59:

60: Bird::Bird(COLOR color, bool migrates, int age):

61:    Animal(age),

62:    itsColor(color), itsMigration(migrates)

63: {

64:    cout << "Bird constructor...n";

65: }

66:

67: class Pegasus : public Horse, public Bird

68: {

69:    public:

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

71:       Pegasus(COLOR, HANDS, bool, long, int);

72:       virtual ~Pegasus() { cout << "Pegasus destructor...n";}

73:       virtual long GetNumberBelievers() const

74:          { return itsNumberBelievers; }

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

76:       virtual int GetAge() const { return Horse::GetAge(); }

77:    private:

78:       long itsNumberBelievers;

79: };

80:

81: Pegasus::Pegasus(

82:    COLOR aColor,

83:    HANDS height,

84:    bool migrates,

85:    long NumBelieve,

86:    int age):

87: Horse(aColor, height,age),

88: Bird(aColor, migrates,age),

89: itsNumberBelievers(NumBelieve)

90: {

91:    cout << "Pegasus constructor...n";

92: }

93:

94: int main()

95: {

96:    Pegasus *pPeg = new Pegasus(Red. 5, true, 10, 2);

97:    int age = pPeg->GetAge();

98:    cout << "This pegasus is " << age << " years old.n";

99:    delete pPeg;

100:   return 0;

101: }

Результат:

Animal constructor...

Horse constructor...

Animal constructor...

Bird constructor...

Pegasus constructor...

This pegasus is 2 years old.

Pegasus destructor.,.

Bird destructor...

Animal destructor...

Horse destructor...

Animal destructor...

Анализ: В листинге содержится ряд интересных решений. Так, в строках 8—17 объявляется новый класс Animal с переменной-членом itsAge и двумя методами — GetAge() и SetAge().

В строке 25 класс Horse производится от класса Animal. Конструктор класса Horse теперь имеет третий параметр age, который передается в базовый класс Animal. Обратите внимание, что в классе Horse метод GetAge() не замещается, а просто наследуется.

В строке 46 класс Bird производится от класса Animal. Конструктор этого класса также содержит параметр age, с помощью которого инициализируется базовый класс Animal. Метод GetAge() также наследуется этим классом без замещения.

Класс Pegasus производится от двух базовых классов Horse и Bird, поэтому с исходным базовым классом Animal он связан двумя линиями наследования. Если для объекта класса Animal будет вызван метод GetAge(), то для преодоления неопределенности нужно точно указать, к какому базовому классу следует обращаться за этим методом, либо метод GetAge() следует заместить в классе Pegasus.

В нашем примере программы метод GetAge() замещается для класса Pegasus таким образом, что в нем явно указывается обращение к аналогичному методу конкретного базового класса.

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

1 ... 81 82 83 84 85 86 87 88 89 ... 170
На этой странице вы можете бесплатно читать книгу Освой самостоятельно С++ за 21 день. - Джесс Либерти бесплатно.

Оставить комментарий