|
Программирование >> Оптимизация возвращаемого значения
Итак, вопрос с приведением типов решен - оно не подходит. Но остается еще одна проблема: несовпадение между типами указателей на функции, которые должен содержать массив HitMap, и указателями на функции hitSpaceShip, hitSpaceStation и hitAsteroid. Существует только один способ исправить ситуацию. Надо изменить функции так, чтобы они все принимали аргументы типа GameObject: class GameObject { Здесь без изменений, public: virtual void collide(GameObjectSc otherObject) = 0; class Spaceship: public GameObject { public: virtual void collide(GameObjectSc otherObject); Теперь параметр всех этих функций имеет тип GameObject. virtual void hitSpaceShip(GameObjectSc spaceship); virtual void hitSpaceStation(GameObjectSc spaceStation); virtual void hitAsteroid(GameObject6c asteroid); Когда проблема двойной диспетчеризации решалась с помощью виртуальных функций, вы перегружали функцию collide. Здесь же вместо этого использован ассоциативный массив указателей на функции - члены класса, поскольку тип параметра один и тот же для всех функции hit, а значит, они должны иметь различные имена. Теперь можно записать функцию initializeCollisionMap так: Spaceship::HitMap * Spaceship::initializeCollisionMap() { HitMap *phm = new HitMap; (*phm) [ Spaceship ] = SchitSpaceShip; (*phm)[ SpaceStation ] = SchitSpaceStation; {*phm) [ Asteroid ] = SchitAsteroid; return phm; К сожалению, параметры функций hit теперь имеют тип GameObject вместо типа производного класса, как раньше. Привести действительность в соответствие с ожиданиями можно с помощью оператора dynamic cast (см. правило 2), примененного в каждой функции: void Spaceship::hitSpaceShip(GameObjectSc spaceship) { SpaceShipSc otherShip= dynamic cast<SpaceShipSc>(spaceship); обработать столкновение SpaceShip-SpaceShip; void Spaceship::hitSpaceStation(GameObjectSc spaceStation) SpaceStationSc station= dYnamic cast<SpaceStationSc>(SpaceStation); обработать столкновение SpaceShip-SpaceStation; void Spaceship::hitAsteroid(GameObjectSc asteroid) { AsteroidSc theAsteroid = dYnamic cast<AsteroidSc> (asteroid) ; обработать столкновение SpaceShip-Asteroid; Выполнение каждого из операторов dynamic cast вызывает генерацию исключения bad cast, если попытка приведения типа окончилась неудачей. Но скорее всего они всегда будут успешными, так как функции hi t никогда не должны вызываться с некорректными типами параметров. Однако лучше все же перестраховаться. Использование ДЛЯ обработки столкновений функций, не являющихся членами класса Теперь вы знаете, как построить ассоциативный массив, похожий на таблицу виртуальных функций, который позволяет реализовать вторую часть двойной диспетчеризации, и как инкапсулировать детали ассоциативного массива в функции поиска. Но, поскольку этот массив содержит указатели на функции - члены класса, вам все равно придется изменять определения класса, если в игру вводится новый тип объектов GameObj ect, а это потребует обязательной перекомпиляции. Например, если в игре появляется тип Satellite, придется добавить к классу Spaceship объявление функции для обработки столкновений между спутниками (satellites) и космическими кораблями (spaceships). Все пользователи класса Spaceship будут вынуждены выполнить перекомпиляцию, даже если в своем варианте игры они обходятся без спутников. Именно из-за этого пришлось отказаться от реализации двойной диспетчеризации, основанной только на виртуальных функциях, хотя такое решение требовало намного меньше усилий, чем то, которое вы только что увидели. Перекомпиляции можно было бы избежать, если бы ассоциативный массив содержал указатели на функции, не являющиеся членами класса. Кроме этого, переход к функциям для обработки столкновений, не являющихся членами класса, позволит решить один до сих пор не рассматривавшийся организационный вопрос: в каком классе должно обрабатываться столкновение между объектами различных типов? В последней из предложенных реализаций столкновение объектов 1 и 2 обрабатывалось в классе 1-го объекта, если этот объект был левым аргументом функции processCollision. Если же левым аргументом функции processCollision был 2-й объект, то столкновение должно было бы обрабатываться в классе для 2-го объекта. Целесообразно ли это? Не Л5ше ли сделать так, чтобы столкновения между объектами А и в обрабатывались в каком-то нейтральном месте вне этих классов? Если вывести функции обработки столкновений из классов, то можно будет предоставить пользователям заголовочные файлы, содержащие определения классов без функций hit или collide. Затем функция processCollision приобретет следующую структуру: #include Spaceship.h #include SpaceStation.h #include Asteroid.h namespace { Неименованное пространство имен - см. ниже. Основные функции обработки столкновений, void shipAsteroid(GameObjectSc spaceship, GameObjectSc asteroid) ; void shipStation(GameObjectSc spaceship, GameObjectSc spaceStation) ; void asteroidStation(GameObjectSc asteroid, GameObjectSc spaceStation) ; Вспомогательные функции обработки столкновений, обеспечивающие симметрию: меняют местами параметры и вызывают основную функцию, void asteroidship(GameObjectSc asteroid, GameObjectSc spaceship) { shipAsteroid(spaceship, asteroid); } void stationShip(GameObjectSc spaceStation, GameObjectSc spaceship) { shipStation(spaceship, spaceStation); } void stationAsteroid(GameObjectSc spaceStation, GameObjectSc asteroid) { asteroidStation(asteroid, spaceStation); } Cm. описание этих типов/функций ниже. typedef void (*HitFunctionPtr) (GameObjectSc, GameObjectSc); typedef map< pair<string,string>, HitFunctionPtr > HitMap; pair<string,string> makeStringPair(const char *sl, const char *s2); HitMap * initializeCollisionMapO; HitFunctionPtr lookup(const stringSc classl, const stringSc class2) ; } Конец пространства имен, void processCollision(GameObjectSc objectl, GameObjectSc object2) HitFunctionPtr phf = lookup(typeid(objectl).name(), typeid(object2).name()); if (phf) phf(objectl, object2); else throw UnknownCollision(objectl, object2);
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |