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



ЗНАЕТЕ ЛИ ВЫ?

Перенос метода вверх по иерархии классов

Поиск

 

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

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

 

Приведение указателя к типу производного класса

 

Продолжая держаться за одиночное наследование, эту проблему можно решить таким образом, что метод Fly() будет вызываться только в случае, если указатель в данный момент связан с объектом Pegasus. Для этого необходимо иметь возможность обратиться к указателю и определить, на какой объект он указывает в текущий момент. Такой подход известен как RTTI (Runtime Type Identification — определение типа при выполнении). Но возможность выполнения RTTI была добавлена только в последние версии компиляторов C++.

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

 

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

 

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

Этот подход, хоть и с неохотой, теперь уже официально признан в C++, и для его реализации добавлен новый оператор — dynamic_cast.

Если в программе создан указатель на объекты базового класса Horse и ему присвоен адрес объекта производного класса Pegasus, то такой указатель можно использовать полиморфно. Чтобы обратиться к методу производного класса, нужно динамически подменить указатель базового класса указателем производного класса с помощью оператора dynamic_cast.

Во время выполнения программы происходит тестирование указателя базового класса. Если устанавливается, что текущий объект, на который ссылается указатель базового класса, в действительности является объектом производного класса, то с этим объектом связывается указатель производного класса. В противном случае указатель производного класса становится нулевым. Пример использования этого подхода показан в листинге 13.2.

Листинг 13.2. Приведение вниз

1: // Листинг 13 2 Использование оператора dynamic_cast

2: // Использование rtti

3:

4: #include <iostream h>

5: enum TYPE { HORSE, PEGASUS };

6:

7: class Horse

8: {

9: public

10: virtual void Gallop(){ cout << "Galloping...\n"; }

11:

12: private:

13: int itsAge;

14: };

15:

16: class Pegasus: public Horse

17: {

18: public:

19:

20: virtual void Fly() { cout << "I can fly! I can fly! I can fly!\n"; }

21: };

22:

23: const mt NumberHorse = 5,

24: int main()

25: {

26: Horse* Ranch[NumberHorse];

27: Horse* pHorse;

28: int choice,i,

29: for (i=0; KNumberHorse, i++)

30: {

31: cout << "(1)Horse (2)Pegasus: ";

32: cin >> choice;

33: if (choice == 2)

34: pHorse = new fegasus;

35: else

36: pHorse = new Horse;

37: Rancfi[i] = pHorse,

38: }

39: cout << "\n";

40: for (i=0; a<NumberHorses; i++)

41: {

42: Pegasus *pPeg = dynamic_cast< Pegasus *> (Ranch[i]);

43: if (pPeg)

43: pPeg->Fly();

44: else

45: cout << "Just a horse\n";

46:

47: delete Ranch[i];

48: }

49: return 0;

50: }

 

Результат:

(1)Horse (2)Pegasus: 1

(1)Horse (2)Pegasus: 2

(1)Horse (2)Pegasus: 1

(1)Horse (2)Pegasus: 2

(1)Horse (2)Pegasus: 1

Just a horse

I can fly! I can fly! I can fly!

Just a horse

I can fly! I can fly! I can fly!

Just a horse

 

Вопросы и ответы

Во время компиляции появляется сообщение об ошибке C4541: 'dynamic_cast' used on polymorphic type 'class Horse' with/GR-; unpredictable behavior may result

Как поступить?

Это сообщение MFC действительно может смутить начинающего программиста. Чтобы устранить ошибку, выполните ряд действий.

1. Выберите в окне проекта команду Project->Settings.

2. Перейдите к вкладке С/C++.

3. Выберите в раскрывающемся списке Category опцию C++ Language

4. Установите Enable Runtime Type Information (RTTI).

5. Повторно скомпилируйте весь проект.

 

Анализ: Этот пример программы также будет вполне работоспособным. Метод Fly() не связан напрямую с классом Horse и не будет вызываться для обычных объектов этого класса. Он выполняется только для объектов класса Pegasus, но для этого программе приходится каждый раз анализировать, с каким объектом связан указатель, и приводить текущий указатель к типу производного класса.

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

 



Поделиться:


Последнее изменение этой страницы: 2016-12-10; просмотров: 370; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 13.59.217.1 (0.006 с.)