|
Программирование >> Дополнительные возможности наследования
Когда вы захотите создать списки объектов классов Bird и Horse, перед вами возникнет еще одна проблема. Хотелось бы, чтобы объект Pegasus был представлен в обоих списках, но в данном случае это невозможно. Для решения возникшей проблемы можно использовать несколько подходов. Например, можно переименовать слишком лошадиный метод GallopO в более обтекаемый MoveO, после чего заместить этот метод в объекте Pegasus таким образом, чтобы он выполнял функцию метода FlyO. В других объектах класса Horse метод MoveO будет выполняться так же, как раньше выполнялся метод GallopO. Для объекта Pegasus можно даже определить, что короткие дистанции он должен преодолевать методом GallopO, а длинные - методом FlyO: Pegasus::Move(long distance) { if (distance > veryFar) fly(distance); else gallop(distance); } Ho и этот подход имеет ряд ограничений, поскольку объект уже не сможет летать на короткие дистанции и бегать на длинные. Может быть, все же просто перенести метод FlyO в класс Horse, как показано в листинге 13.1? Проблема состоит в том, что лошади, в большинстве своем, летать не умеют, поэтому во всех объектах этого класса, за исключением объекта Pegasus, данный метод не должен ничего выполнять. Явсшииг 13.1. Имеют itu Aomagu деташь... 2 3 4 5 6 7 8 9 1б 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Листинг 13.1. Умеют ли лошади летать... Фильтрация метода Fly() в классе Horse #include <iostreatn.h> class Horse { public: void Gallop(){ cout Galloping...\ n ; > virtual void Fly() { cout Horses cant fly.\ n ; > private: int itsAge; class Pegasus : public Horse { public: virtual void Fly() { cout I can fly! I can fly! Г can ®fly!\ n ; } } ; const int NumberHorses = 5; int [tiainO Horse* Ranch[Nu[nberHorses];
Horses cant fly. I can fly! I can fly! I can fly! Horses cant fly. I can fly! I can fly! I can fly! Horses cant fly. Безусловно, эта профамма будет работать ценой добавления в класс Horse редко используемого метода Fly(). Это произошло в сфоке 10. Для объектов данного класса этот метод констатирует факт, что лошади летать не умеют. И только для объекта Pegasus метод замещается в сфоке 18 таким образом, что при вызове его объект заявляет, что умеет летать. В строке 24 используется массив указателей на объекты класса Horse, с помощью которого метод Fly() вызывается для разных объектов класса. В зависимости от того, для какого из объектов в данный момент вызывается метод, профамма выводит на экран разные сообщения. Показанный выше пример программы был значительно сокращен, чтобы выделить именно те моменты, которые сейчас рассматриваются. Так, для простоты программы из нее были удалены конструктор и виртуальные деструкторы. Перенос метода вверх по иерархии классов Очень часто для решения подобных проблем объявление метода переносят вверх по иерархическому списку классов, чтобы сделать его доступным большему числу производных классов. Но при этом есть угроза, что базовый класс уподобится кладовке, захламленной старыми вещами. Такой подход делает программу громоздкой и нарушает саму идею иерархии классов в С++, когда производные классы дополняют своими функциями небольшой набор общих функций базового класса. Противоречие состоит в том, что при переносе функции из производных классов вверх по иерархии в базовый класс трудно сохранить уникальность интерфейсов производных классов. Так, можно предположить, что у наших двух классов Bird и Horse есть базовый класс Animal, в котором собраны функции, общие для всех производных классов, например функция питания - Eat(). Перенеся метод FlyO в базовый класс, придется позаботиться о том, чтобы этот метод вызывался только в некоторых производных классах. Приввдвннв указатвля к mung пронзводногв класса Продолжая держаться за одиночное наследование, эту проблему можно решить таким образом, что метод Fly() будет вызываться только в случае, если указатель в данный момент связан с объектом Pegasus. Для этого необходимо иметь возможность обратиться к указателю и определить, на какой объект он указывает в текущий момент. Такой подход известен как RTTI (Runtime Туре Identification - определение типа при выполнении). Но возможность вьшолнения RTTI была добавлена только в последние версии компиляторов С++. Если ваш компилятор не поддерживает RTTI, можете реализовать его собственными силами, добавив в программу метод, который возвращает перечисление типов каждого класса. Возвращенное значение можно анализировать во время выполнения программы и допускать вызов метода Fly() только в том случае, если возвращается значение Pegasus. примечание Не злоупотребляйте использованием RTTI в своих программах, так как этот подход рассматривается как аварийный и свидетельствует о том, что структура программы изначально была плохо продумана. Профессиональный программист предпочтет использование виртуальных функций, шаблонов или множественного наследования, речь о котором пойдет ниже в этой главе. Чтобы вызвать метод Fly(), необходимо во время выполнения изменить тип указателя, определив, что он связан не с объектом Horse, а с объектом производного класса Pegasus. Этот способ называют приведением вниз, поскольку объект базового класса Horse приводится к объекту производного класса Pegasus. Этот подход, хоть и с неохотой, теперь уже официально признан в С++, и для его реализации добавлен новый оператор - dynamio oast. Если в программе создан указатель на объекты базового класса Horse и ему присвоен адрес объекта производного класса Pegasus, то такой указатель можно использовать полиморфно. Чтобы обратиться к методу производного класса, нужно динамически подменить указатель базового класса указателем производного класса с помощью оператора dynamic oast.
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |