Программирование >>  Оптимизация возвращаемого значения 

1 ... 76 77 78 [ 79 ] 80 81 82 ... 96


Обратите внимание на неименованное пространство имен, содержащее функции для реализации processCollision. Все, находящееся в таком неименованном пространстве имен, будет закрытым внутри текущего модуля трансляции (по сути, в текущем файле) - это то же самое, если бы функции были объявлены как static для файла. Но с введением пространств имен такое объявление устарело, и вам следует привыкать использовать неименованные пространства имен, если ваши компиляторы поддерживают их.

Концептуально такая реализация очень похожа на реализацию с использованием функций - членов класса, за исключением небольших различий. Во-первых, HitFunctionPtr теперь является указателем на функцию, не являющуюся членом класса. Во-вторых, класс исключений CollisionWithUnknownObject переименован в UnknownCollision и имеет в качестве аргументов два объекта, а не один. И наконец, функция lookup обладает двумя параметрами и выполняет обе части двойной диспетчеризации. Это означает, что карта столкновений должна теперь содержать три блока данных: два имени типов и указатель HitFunctionPtr.

Но класс тар способен включать только два блока данных. Обойти возникшую проблему можно с помощью стандартного шаблона pair, который позволяет упаковать два имени типа в один объект initializeCollisionMap. Вместе со вспомогательной функцией makeStringPair этот объект будет выглядеть так:

/ / Эта функция создает пару объектов

pair<string,string> из двух строк char*.

Эти пары используются позже в функции initializeCollisionMap. Обратите внимание,что данная функция позволяет выполнять оптимизацию возвращаемого значения (см. правило 20).

namespace { Неименованное пространство имен - см. ниже.

pair<string,string> makeStringPair(const char *sl,

const char *s2)

{ return pair<string,string>(si, s2); } } Конец пространства имен.

namespace { Еще одно неименованное пространство имен.

HitMap * initializeCollisionMapО {

HitMap *phm = new HitMap;

(*phm)[makeStringPair( Spaceship , Asteroid )] = ScshipAsteroid;

(*phm)[makeStringPair( Spaceship , SpaceStation )] = StshipStation;

return phm;

} Конец пространства имен.

Нужно изменить также функцию lookup, чтобы она работала с объектами pair<string, string>, которые теперь включают первый компонент карты столкновений:



namespace { Это будет объяснено ниже.

HitFunctionPtr lookup(const stringSc classl, const stringSc class2)

static auto ptr<HitMap> collisionMap(initializeCollisionMap()); Cm. ниже описание функции make pair. HitMap::iterator mapEntry=

collisionMap->find(make pair (classl, class2)); if (mapEntry == collisionMap->end()) return 0; return {*mapEntry).second; ,

} Конец пространства имен.

Это почти то же, чем вы располагали раньше. Единственное различие состоит в использовании функции make pair в операторе:

HitMap::iterator mapEntry=

collisionMap->find(make pair(classl, class2));

Функция (шаблон) make pai г введена в стандартную библиотеку только для удобства (см. правило 35) и избавляет вас от хлопот по заданию типов при создании объекта pair. С тем же успехом можно было бы записать этот оператор следующим образом:

HitMap::iterator mapEntry=

collisionMap->find(pair<string,string>(classl, class2));

Ho такая запись длиннее, и задание типов для pair является избыточным (они совпадают с типами для classl и class2), поэтому чаще используется форма с функцией make pair.

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

Итак, цель достигнута. Если в иерархию добавляются новые под классы класса GameObj ect, то существующие классы не будут нуждаться в перекомпиляции (если они не собираются использовать новые классы). Вы избавились от неразберихи, которая возникает при использовании переключателя switch, основного на RTTI, и поддержке условных операторов if-then-else. Добавление к иерархии новых классов требует только хорошо определенных и локализованных изменений в системе: одной или двух вставок в функции initializeCollisionMap и объявления новых функций для обработки столкновений в неименованном пространстве имен, связанном с реализацией функции processCollision. Чтобы дойти до этого места, потребовалось много усилий, но, согласитесь, путешествие того стоило.



Наследование и эмулированные таблицы виртуальных функций

Осталось устранить последнюю проблему. (Впрочем, механизм реализации виртуальных функций настолько сложен, что практически всегда за последней проблемой будет всплывать еще какая-нибудь.) Все что вы сделали, будет прекрасно действовать до тех, пор пока вам не понадобится разрешить при вызове функций для обработки столкновений преобразование типов, основанное на наследовании. Но предположим, что в создаваемой игре иногда требуется различать военные и гражданские космические корабли. Можно было бы модифицировать иерархию, руководствуясь правилом 33 и сделав реальные классы Commercial Ship (Гражданский корабль) и MilitaryShip (Военный корабль) наследниками нового абстрактного класса Spaceship (см. рис. 5.20).


(SpacoShip (acoStau (steroid

/ Cornmetci.-il Ч V. Ship .


Military >v Ship J

Допустим, что гражданские и военные корабли при столкновении с чем-либо ведут себя одинаково. Следовательно, они смогут использовать те же функции обработки столкновений, которые применялись до добавления классов CommercialShip и MilitaryShip. В таком случае при столкновении, например, объектов MilitaryShip и Asteroid должна вызываться функция:

void shipAsteroid(GameObjectSc spaceship, GameObjectSc asteroid) ;

Ho это не так. В действительности будет сгенерировано исключкние Unknown-Col 1 i s ion. Это произойдет потому, что функция lookup должна будет найти функции, соответствующие типам с именами MilitaryShip и Asteroid, а в массиве collisionMap нет таких функций. Даже если с объектом MilitaryShip разрешалось бы обращаться как с объектом Spaceship, функция lookup не может ничего знать об этом.

Если вам нужно реализовать двойную диспетчеризацию, и при этом поддерживать основанное на наследовании преобразование параметров, то единственным



1 ... 76 77 78 [ 79 ] 80 81 82 ... 96

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика