Листинг 11.7. Явное обращение к методу базового класса



1: //Листинг 11.7. Явное обращение к методу базового класса

2:

3: #include <iostream.h>

4:

5: class Mammal

6: {

7: public:

8: void Move() const { cout << "Mammal move one step\n"; }

9: void Move(int distance) const

10: {

11: cout << "Mammal move " << distance;

12: cout << " steps.\n";

13: }

14:

15: protected:

16: int itsAge;

17: int itsWeight;

18: };

19:

20: class Dog : public Mammal

21: {

22: public:

23: void Move()const;

24:

25: };

26:

27: void Dog::Move() const

28: {

29: cout << "In dog move...\n";

30: Mammal::Move(3);

31: }

32:

33: int main()

34: {

35: Mammal bigAnimal;

36: Dog fido;

37: bigAnimal.Move(2);

38: fido.Mammal::Move(6);

39: return 0;

40: }

 

Результат:

Mammal move 2 steps.

Mammal move 6 steps.

 

Анализ: В строке 35 создается объект bigAnimal класса Mammal, а в строке 36 — объект fido класса Dog. В строке 37 вызывается метод Move(int) из базового класса для объекта класса Dog.

В предыдущей версии программы мы столкнулись с проблемой из-за того, что в классе Dog доступен только один замещенный метод Move(), в котором не задаются параметры. Проблема была разрешена явным обращением к методу Move(int) базового класса в строке 38.

 

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

 

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

 

 

Виртуальные методы

 

В этой главе неоднократно подчеркивалось, что объекты класса Dog одновременно являются объектами класса Mammal. До сих пор под этим подразумевалось, что объекты класса Dog наследуют все атрибуты (данные) и возможности (методы) базового класса. Но в языке C++ принципы иерархического построения классов несут в себе еще более глубинный смысл.

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

Mammal* pMammal = new Dog;

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

 

Примечание: В этом суть полиморфизма. Например, можно объявить множество окон разных типов, включая диалоговые, прокручиваемые окна и поля списков, после чего создавать их в программе с помощью единственного виртуального метода draw(). Создав указатель на базовое окно и присваивая этому указателю адреса объектов производных классов, можно обращаться к методу draw() независимо от того, с каким из объектов в данный момент связан указатель. Причем всегда будет вызываться вариант метода, специфичный для класса выбранного объекта.

 

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

Листинг 11.8. Использование виртуальных методов

1: //Листинг 11.8. Использование виртуальных методов

2:

3: #include<iostream.h>

4:

5: class Mammal

6: {

7: public:

8: Mammal():itsAge(1) { cout << "Mammal constructor...\n"; }

9: virtual ~Mammal() { cout << "Mammal destructor...\n"; }

10: void Move() const { cout << "Mammal move one step\n"; }

11: virtual void Speak() const { cout << "Mammal speak!\n"; }

12: protected:

13: int itsAge;

14:

15: };

16:

17: class Dog : public Mammal

18: {

19: public:

20: Dog() { cout << "Dog Constructor...\n"; }

21: virtual ~Dog() { cout << "Dog destructor...\n"; }

22: void WagTail() { cout << "Wagging Tail...\n"; }

23: void Speak()const { cout << "Woof!\n"; }

24: void Move()const { cout << "Dog moves 5 steps...\n"; }

25: };

26:

27: int main()

28: {

29:

30: Mammal *pDog = new Dog;

31: pDog->Move();

32: pDog->Speak();

33:

34: return 0;

35: }

 

Результат:

Mammal constructor...

Dog Constructor...

Mammal move one step

Woof!

 

Анализ: В строке 11 объявляется виртуальный метод Speak() класса Mammal. Предполагается, что данный класс должен быть базовым для других классов. Вероятно также, что данная функция может быть замещена в производных классах.

В строке 30 создается указатель класса Mammal (pDog), но ему присваивается адрес нового объекта производного класса Dog. Поскольку собака является млекопитающим, это вполне логично. Данный указатель затем используется для вызова функции Move(). Поскольку pDog известен компилятору как указатель класса Mammal, результат получается таким же, как при обычном вызове метода Move() из объекта класса Mammal.

В строке 32 через указатель pDog делается обращение к методу Speak(). В данном случае метод Speak() объявлен как виртуальный, поэтому вызывается вариант функции Speak(), замещенный в классе Dog.

Это кажется каким-то волшебством. Хотя компилятор знает, что указатель pDog принадлежит классу Mammal, тем не менее происходит вызов версии функции, объявленной в другом производном классе. Если создать массив указателей базового класса, каждый из которых указывал бы на объект своего производного класса, то, обращаясь попеременно к указателям данного массива, можно управлять выполнением всех вариантов замещенного метода. Эта идея реализована в листинге 11.9.


Дата добавления: 2019-02-12; просмотров: 237; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!