2012-02-05 2 views
1

В настоящее время я программирую систему прохождения событий для игры на C++, и я думал, что это будет полезно, если события наследуются друг от друга логически.Система C++ для передачи событий с иерархией наследования

Это означает, что я мог бы, например, вызвать событие типа NukeExplosion, который вытекает из Explosion (который, вероятно, выводит из пустого базового класса Event), и было бы получить передаются все слушателям к событию типа NukeExplosion, а также как более общий Explosion.

До сих пор я был в состоянии придумать два возможных решения:

  • Выполнение dynamic_cast на события для каждого набора слушателей к тому же типу события. Если это удастся, я могу передать событие всем слушателям в наборе.
  • Добавление фрагмента кода в каждый тип события, который вызывает событие снова, но с более общим типом. Затем событие будет передано слушателям с использованием результата оператора typeid в сочетании с картой слушателей.

Мне не нравится второй вариант, потому что он подвержен ошибкам и требует, чтобы я написал почти тот же код в каждом классе событий.

Проблема с первым вариантом заключается в том, что, возможно, потребуется сделать много dynamic_cast s, и я хотел бы избежать этого.

Итак, есть ли какой-либо другой способ, который я не принял, или это первый вариант, который я могу сделать лучше всего? Или я должен полностью отказаться от наследования событий?

ответ

1

Я пришел сюда почти с этим вопросом. Проблема в том, что C++ не позволяет функции вроде handle (ExplosionEvent *e) принимать аргумент e со статическим типом Event *, даже если динамический тип e равен ExplosionEvent *. Это была бы хорошая функция, но я не совсем уверен, что еще нужно будет изменить на этом языке.

Шаблон посетителя - это самое чистое решение, о котором я могу думать. Недостатки в том, что он многословный и что он не может быть дешевле, чем dynamic_cast<>.

Main.hpp:

#include <iostream> 

class Event; 
class Handler; 

#include "Event.hpp" 
#include "Handler.hpp" 

событий.HPP:

#ifndef EVENT_H 
#define EVENT_H 

class Event 
{ 
public: 
    virtual void accept (Handler *handler) { } 
}; 

class ExplosionEvent : public Event 
{ 
    void accept (Handler *handler); 
}; 

#endif // !EVENT_H 

Event.cpp:

#include "Main.hpp" 

void 
ExplosionEvent::accept (Handler *handler) 
{ 
    handler->handleExplosion (this); 
} 

Handler.hpp:

#ifndef HANDLER_H 
#define HANDLER_H 

class Handler 
{ 
public: 
    void handle (Event *event) { event->accept (this); } 
    virtual void handleExplosion (ExplosionEvent *explosionEvent) { } 
}; 

class ExplosionHandler : public Handler 
{ 
    void handleExplosion (ExplosionEvent *explosionEvent); 
}; 

#endif // !HANDLER_H 

Handler.cpp:

#include "Main.hpp" 

void 
ExplosionHandler::handleExplosion (ExplosionEvent *explosionEvent) 
{ 
    std::cout << "BOOM!" << std::endl; 
} 

main.cpp:

#include "Main.hpp" 

int 
main (int argc, char *args) 
{ 
    Event *event = new ExplosionEvent; 
    Handler *handler = new ExplosionHandler; 
    handler->handle (event); 
} 

компилировать и запускать:

$ g++ -o boom *.cpp 
$ ./boom 
BOOM! 
$ 
+0

Хотя я уже использовал второй подход, который я описал, мне действительно нравится это решение. Единственная проблема, которую я вижу, - это то, что в системе с большим количеством обработчиков пустые функции в Handler, вероятно, будут вызваны довольно много. – f4st

0

Выполнение динамического каста для каждого слушателя станет очень дорогим для большого количества слушателей, поэтому вам, вероятно, придется в любом случае реализовать карту typeid для слушателей.

У моего слушателя будет HandleEvent, который займет Event. Этот метод будет использовать (с static_cast) базовым событием для типа события, которого он ожидает (ему нужно доверять диспетчеру событий для механизма регистрации событий).

Я бы также реализовал метод в классе Event, который вернул бы новое базовое событие, если оно действительное, потому что вы можете не захотеть отправить базу Event. Это можно сделать с помощью макроса, но я чувствую, что это также можно сделать с помощью шаблона, хотя я еще не смог его запустить. Диспетчер затем получит это базовое событие перед вызовом обработчиков событий, а затем вызовет обработчики для базового события.

+0

Я чувствую, что то, что вы предлагаете, что я описал, как мой второй вариант. И, как я уже сказал, мне это не нравится, потому что это требует добавления подобного кода ко всем классам событий. С другой стороны, это быстрое и довольно простое решение. – f4st

Смежные вопросы