Почему мы, включая меня, всегда делаем такие простые проблемы такими сложными?
Возможно, я не здесь. Но я должен задаться вопросом: действительно ли это лучший дизайн для ваших нужд?
В целом наследование только по функциям может быть лучше достигнуто с помощью указателей на функции / методы или агрегирования / делегирования и передачи объектов данных, чем посредством полиморфизма.
Полиморфизм - очень мощный и полезный инструмент. Но это только один из многих доступных нам инструментов.
Похоже, что каждому подклассу Packet потребуется собственный код Marshalling и Unmarshalling. Возможно, унаследовал код Маршаллинга / Unmarshalling Пакета? Возможно продлить это? Все сверху handle () и все остальное, что требуется.
Это много кода.
Несмотря на то, что он значительно более хитрый, он может быть короче и быстрее для реализации данных Пакета в качестве атрибута struct / union класса Packet.
Маршаллинг и демаршаллинг тогда будут централизованы.
В зависимости от вашей архитектуры это может быть так же просто, как запись (и данные). Предполагая, что между вашими клиент-серверными системами нет больших / мало-порядковых проблем, и нет проблем с заполнением. (Например, sizeof (data) одинаков в обеих системах.)
Запись (& data) / read (& data) является подверженной ошибкам техникой . Но часто это очень быстрый способ написать первый набросок. Позже, когда позволит время, вы можете заменить его на код типа Marshalling / Unmarshalling, основанный на типах для каждого атрибута.
Также: Я занялся хранением данных, которые отправляются / принимаются как структура. Вы можете побитово скопировать структуру с помощью operator = (), что иногда было ОЧЕНЬ полезно! Хотя, возможно, не так много в этом случае.
В конечном итоге вы будете иметь оператор switch где-то в этом типе идентификатора подкласса. Заводской метод (который сам по себе довольно мощный и полезный) делает этот переключатель за вас, ища необходимый метод / объект clone () или copy ().
ИЛИ Вы можете сделать это самостоятельно в пакете. Вы можете просто использовать что-то простое:
(getHandlerPointer (id)) (это)
Еще одним преимуществом подхода к этому ключу (указатели функций), помимо быстрого времени разработки, является то, что вам не нужно постоянно выделять и удалять новый объект для каждого пакета. Вы можете повторно использовать один объект пакета снова и снова. Или вектор пакетов, если вы хотите поставить их в очередь. (Имейте в виду, я бы очистил объект Packet перед повторным вызовом read ()! Просто для безопасности ...)
В зависимости от плотности сетевого трафика в вашей игре распределение / освобождение может быть дорогим. Опять же, преждевременная оптимизация - корень всего зла. И вы всегда можете просто свернуть свои собственные операторы new / delete. (Еще больше затрат на кодирование ...)
Что вы теряете (с помощью указателей на функции), так это чистое разделение каждого типа пакета. В частности, возможность добавлять новые типы пакетов без изменения существующего кода / файлов.
Пример кода:
class Packet
{
public:
enum PACKET_TYPES
{
STATE_PACKET = 0,
PAUSE_REQUEST_PACKET,
MAXIMUM_PACKET_TYPES,
FIRST_PACKET_TYPE = STATE_PACKET
};
typedef bool ( * HandlerType ) ( const Packet & );
protected:
/* Note: Initialize handlers to NULL when declared! */
static HandlerType handlers [ MAXIMUM_PACKET_TYPES ];
static HandlerType getHandler( int thePacketType )
{ // My own assert macro...
UASSERT( thePacketType, >=, FIRST_PACKET_TYPE );
UASSERT( thePacketType, <, MAXIMUM_PACKET_TYPES );
UASSERT( handlers [ thePacketType ], !=, HandlerType(NULL) );
return handlers [ thePacketType ];
}
protected:
struct Data
{
// Common data to all packets.
int number;
int type;
union
{
struct
{
int foo;
} statePacket;
struct
{
int bar;
} pauseRequestPacket;
} u;
} data;
public:
//...
bool readFromSocket() { /*read(&data); */ } // Unmarshal
bool writeToSocket() { /*write(&data);*/ } // Marshal
bool handle() { return ( getHandler( data.type ) ) ( * this ); }
}; /* class Packet */
PS: Вы можете покопаться в Google и взять cdecl / c ++ decl. Это очень полезные программы. Особенно при игре с указателями функций.
например:.
c++decl> declare foo as function(int) returning pointer to function returning void
void (*foo(int ))()
c++decl> explain void (* getHandler( int ))( const int & );
declare getHandler as function (int) returning pointer to function (reference to const int) returning void