Как «десериализовать» производный класс из сериализованных данных? - PullRequest
9 голосов
/ 17 июля 2010

Как «десериализовать» производный класс из сериализованных данных? Или, может быть, я должен сказать, есть ли лучший способ «десериализации» данных в производные классы?

Например, предположим, у вас был чистый виртуальный базовый класс (B), который наследуется тремя другими классами, X, Y и Z. Более того, у нас есть метод serialize (), который будет переводить X: B, Y : B и Z: B в сериализованные данные.

Таким образом его можно перенести через сокет, именованный канал и т. Д. К удаленному процессу.

У меня проблема в том, как создать соответствующий объект из сериализованных данных?

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

Например:

B deserialize( serial_data )
{
    parse the derived type from the serial_data

    switch (derived type)
        case X
            return X(serial_data)
        case Y
            return Y(serial_data)
        case Z
            return Z(serial_data)
}

Итак, после изучения производного типа объекта мы вызываем соответствующий конструктор производного типа.

Однако, это чувствует себя неловко и громоздко. Я надеюсь, что есть более красноречивый способ сделать это. Есть ли?

Ответы [ 2 ]

2 голосов
/ 17 июля 2010

На самом деле это более общая проблема, чем сериализация, называемая Virtual Constructor.

. Традиционный подход к Factory, который на основе идентификатора возвращает правильный производный тип.Существует два решения:

  • метод switch, как вы заметили, хотя вам нужно выделить в куче
  • метод prototype

Метод-прототип выглядит следующим образом:

// Cloneability
class Base
{
public:
  virtual Base* clone() const = 0;
};

class Derived: public Base
{
public:
  virtual Derived* clone() const { return new Derived(*this); }
};

// Factory
class Factory
{
public:
  Base* get(std::string const& id) const;
  void set(std::string const& id, Base* exemplar);

private:
  typedef std::map < std::string, Base* > exemplars_type;
  exemplars_type mExemplars;
};

Несколько традиционно Factory делают синглтоном, но это совсем другое дело.

Для правильной десериализации проще, если выиметь виртуальный метод deserialize для вызова объекта.

РЕДАКТИРОВАТЬ: Как работает Фабрика?

В C ++ вы не можете создать тип, о котором вы не знаете.Поэтому идея выше заключается в том, что задача создания объекта Derived передается классу Derived с помощью метода clone.

Далее следует Factory.Мы собираемся использовать map, который будет ассоциировать «тег» (например, "Derived") с экземпляром объекта (скажем, Derived здесь).

Factory factory;
Derived derived;
factory.set("Derived", &derived);

Теперь, когда мыЕсли мы хотим создать объект, тип которого мы не знаем во время компиляции (поскольку тип определяется на лету), мы передаем тег фабрике и запрашиваем взамен объект.

std::unique_ptr<Base> base = factory.get("Derived");

Под крышкой Factory найдет Base*, связанный с тегом "Derived", и вызовет метод clone объекта.Это фактически (здесь) создаст объект типа времени выполнения Derived.

. Мы можем проверить это с помощью оператора typeid:

assert( typeid(base) == typeid(Derived) );
0 голосов
/ 17 июля 2010
inmemory:
--------
type1 {
  chartype a;
  inttype b;
};
serialize(new type1());

serialized(ignore { and ,):
---------------------------
type1id,len{chartypeid,adata,inttypeid,bdata}

Полагаю, в идеальном протоколе сериализации каждый не примитивный тип должен иметь префикс typeid, len. Даже если вы сериализуете один тип, который не является производным от чего-либо, вы добавите идентификатор типа, потому что другой конец должен знать, какой тип он получает (независимо от структуры наследования). Таким образом, вы должны упомянуть производные идентификаторы классов в сериализации, потому что логически это разные типы. Поправь меня, если я ошибаюсь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...