Освой самостоятельно С++ за 21 день.
Шрифт:
В строке 24 используется массив указателей на объекты класса Horse, с помощью которого метод Fly вызывается для разных объектов класса. В зависимости от того, для какого из объектов в данный момент вызывается метод, программа выводит на экран разные сообщения.
Примечание:Показанный выше пример программы был значительно сокращен, чтобы выделить именно те моменты, которые сейчас рассматриваются. Так, для простоты программы из нее были удалены конструктор и виртуальные деструкторы.
Перенос
Очень часто для решения подобных проблем объявление метода переносят вверх по иерархическому списку классов, чтобы сделать его доступным большему числу производных классов. Но при этом есть угроза, что базовый класс уподобится кладовке, захламленной старыми вещами. Такой подход делает программу громоздкой и нарушает саму идею иерархии классов в 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
Как поступить?