Проблемы с динамическим приведением от родителя к ребенку


Я работаю над базовым клиент-серверным приложением на языке C++, использующим сокеты, которые будут запускать игру battleship. Вся коммуникация между клиентом и сервером осуществляется в виде простой иерархии объектов, которая выглядит примерно так:

namespace Message {
    enum MessageType { BASE, RESULT };

    class BattleshipMessage {
    public:

        virtual MessageType get_type() { return BASE; }
        virtual ~BattleshipMessage() {}
    };

    class ResultMessage : public BattleshipMessage {
    public:
        char _attackResult;

        ResultMessage( char result ) : _attackResult( result ) {}
        virtual MessageType get_type() { return RESULT; }

        ~ResultMessage() {}
    };
}

При получении объекта, отправленного через сокет, я хотел бы иметь возможность получить его как универсальный BattleshipMessage, а затем на основе MessageType, возвращенного get_type (), привести его к соответствующему дочернему классу, чтобы получить все, что угодно дополнительная информация, которая может понадобиться для обработки сообщения. Я боюсь, что C# сделал меня мягким, поскольку я сделал это с легким синтаксисом, таким как ResultMessage result = message as ResultMessage, однако я застрял, пытаясь заставить мою реализацию C++ работать.

BattleshipMessage* message;
recv( hAccepted, reinterpret_cast<char*>( &message ), sizeof(message), 0 );

switch ( message->get_type() ) {
case RESULT:
    if ( ResultMessage* result = dynamic_cast<ResultMessage*>(message)) {
        std::string celebration = "HOORAY!";
    }
    break;
}

Я получаю Access violation reading location, когда разыменовываю указатель и пытаюсь вызвать get_type(). Может ли кто-нибудь указать мне правильное направление?

2 3

2 ответа:

sizeof(message)

Задает размер указателя, который обычно является 32-разрядным или 64-разрядным в зависимости от вашей машины. Вы хотите

sizeof(BattleshipMessage)

, что дает размер класса. Даже тогда я не уверен, что это правильный подход, так как каждый объект класса будет содержать указатель на vtable, который обрабатывает вызовы динамической диспетчерской/виртуальной функции, и отправка класса через машины с использованием метода необработанного приведения, который вы используете, сделает этот указатель недействительным.

Я думаю, что вы должны сначала сериализовать ваш объект (т. е. преобразовать его в поток символов) перед отправкой по сети, а затем десериализовать для восстановления класса: можно ли сериализовать и десериализовать класс В C++?

Избавьтесь от всех виртуальных методов и сохраните MessageType в качестве переменной-члена в BattleshipMethod. Тогда ваш код должен работать.