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

1 ... 73 74 75 [ 76 ] 77 78 79 ... 96


успешной реализации класса Spaceship, классы SpaceStation и Asteroid можно реализовать аналогично.

В функции SpaceShip::collide нужно каким-то образом сопоставить динамическому типу параметра otherObject указатель на соответствующую функцию для обработки столкновений. Простейший способ сделать это заключается в создании ассоциативного массива, который дает указатель на соответствующую функцию - член класса по заданному имени класса. Можно напрямую реализовать функцию collide при помощи такого ассоциативного массива, но Л5ше добавить промежуточную функцию lookup, принимающую объект GameObject и возвращающую соответствующий указатель на функцию. То есть вы передаете функции lookup объект GameObj ect, и она возвращает указатель на функцию - член класса, которая будет вызываться, если вы столкнетесь с чем-то типа GameObject.

Функция lookup объявляется так:

class Spaceship: public GameObject { private:

typedef void (Spaceship::*HitFunctionPtr)(GameObject&); static HitFunctionPtr lookup(const GameObject& whatWeHit);

Синтаксис указателей на функции никогда не бывает особенно удобным, а для указателей на функции - члены класса он еще хуже, чем обычно, поэтому директивой typedef HitFunctionPtr был определен тип, который служит сокращением для указателя на функцию - член класса Spaceship, принимающую в качестве параметра GameObj ееt& и ничего не возвращающую.

После того как получена функция lookup, реализация функции collide становится легче легкого:

void Spaceship::collide(GameObject& otherObject) {

HitFunctionPtr hfp =

lookup(otherObject); Найти функцию,

if (hfp) { Если функция найдена,

(this->*hfp)(otherObject); вызвать ее.

else {

throw CollisionWithUnknownObject(otherObject);

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



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

Такой массив должен создаваться и инициализироваться до того, как он будет использоваться, и уничтожаться, когда он больше не нужен. Можно было бы создавать и удалять массив врзную с помощью операторов new и delete, но это иногда приводит к ошибкам: как гарантировать, что массив не был использован прежде, чем вы соберетесь проинициализировать его? Jljme автоматизировать этот процесс при помощи компилятора, объявив ассоциативный массив статическим в функции lookup. На этот раз он будет создаваться и инициализироваться при первом вызове функции lookup и автоматически уничтожаться через какое-то время после выхода из функции main.

Кроме того, вы можете использовать в качестве ассоциативного массива шаблон тар из стандартной библиотеки шаблонов (см. правило 35):

class Spaceship: public GameObject { private:

typedef void (Spaceship::*HitFunctionPtr)(GameObject&); typedef map<string, HitFunctionPtr> HitMap;

Spaceship::HitFunctionPtr

Spaceship::lookup(const GameObjectSc whatWeHit) {

static HitMap collisionMap;

Здесь collisionMap и является ассоциативным массивом. Он сопоставляет имени класса (как объекту типа string) указатель на функцию - член класса Spaceship. Так как выговорить map<string, HitFunctionPtr> нелегко, было использовано определение типа. (Можете попытаться в качестве развлечения переписать определение collisionMap, отказавшись от определения типов HitMap и HitFunctionPtr. Немногие отважатся сделать это дважды.)

После создания collisionMap реализация функции lookup становится достаточно простой. Это связано с тем, что операция поиска непосредственно поддерживается классом тар, и единственная переносимая функция - член класса, с помощью которой можно всегда проконтролировать результат вызова typeid. Это функция name (она, как и следовало ожидать*, возвращает имя динами-

* Оказывается, что это не столь предсказуемо. Стандарт языка С++ не определяет возвращаемое функцией type info: :name значение, и различные реализации ведут себя по-разному. (В одной из реализаций, например, функция type inf о: : name возвращает class Spaceship, если задан класс Spaceship.) Лучше было бы идентифицировать класс по адресу связанного с ним объекта type inf о, так как этот адрес гарантированно является уникальным. Тогда тип HitMap объявлялся бы как map<const type info* ,HitFunctionPtr>.



ческого типа объекта). Тогда для реализации функции lookup достаточно найти в массиве collisionMap ячейку, соответствующую динамическому типу аргумента функции lookup.

Код для функции lookup довольно прост, но если вы не знакомы со стандартной библиотекой шаблонов (см. правило 35), он может вызвать у вас затруднения. Не беспокойтесь. Комментарии в функции объясняют, что происходит.

Spaceship::HitFunctionPtr

Spaceship::lookup(const GameObjectSc whatWeHit) {

static HitMap collisionMap; Вы увидите инициализацию

collisionMap позже. Поиск функции обработки столкновения для типа whatWeHit. Возвращаемое значение - это похожий на указатель объект, который называется итератором (см. правило 35) . HitMap::iterator mapEntry=

collisionMap.find(typeid(whatWeHit).name()); mapEntry == collisionMap.end() , если поиск был неудачным; / / это стандартное поведение для тар. Снова см. правило 35.

if (mapEntry == collisionMap.end()) return 0;

Если вы оказались здесь, поиск был успешным. mapEntry указывает

на полную ячейку карты, состоящую из пары

(string, HitFunctionPtr) . Вам нужна только вторая часть

пары, поэтому она и возвращается.

return (*mapEntry).second;

Последний оператор в функции возвращает ( *mapEntrY) . second вместо более привычного mapEntry->second, чтобы удовлетворить капризам стандартной библиотеки шаблонов. Подробнее см. на стр. 108.

Инициализация

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

Теперь настала пора инициализировать массив collisionMap. Вы, наверное, хотели бы написать что-то вроде:

Неправильная реализация. Spaceship::HitFunctionPtr

Spaceship: : lookup (const GameObjectSc whatWeHit) {

static HitMap collisionMap; collisionMap[ Spaceship ] = SchitSpaceShip; collisionMap[ SpaceStation ] = SchitSpaceStation; collisionMap[ Asteroid ] = SchitAsteroid;



1 ... 73 74 75 [ 76 ] 77 78 79 ... 96

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