Как Вы 'десериализовываете' производный класс от сериализированных данных? Или возможно я должен сказать, там лучший способ 'десериализовать' данные в производные классы?
Например, предположите, что у Вас был чистый виртуальный базовый класс (B), который наследован тремя другими классами, X, Y и Z. Кроме того, мы имеем метод, сериализируем (), который переведет X:B, Y:B и Z:B в сериализированные данные.
Таким образом, это может быть убито через сокет, именованный канал, и т.д. к удаленному процессу.
Проблема, которую я имею, как мы создаем соответствующий объект из сериализированных данных?
Единственное решение, которое я могу предложить, включает идентификатор в сериализированные данные, которые указывают на заключительный тип производного объекта. Где получатель, первые синтаксические анализы поле производного типа от сериализированных данных, и затем использует оператор переключения (или своего рода логика как этот) для вызова соответствующего конструктора.
Например:
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)
}
Таким образом, после изучения производного объекта вводят, мы вызываем соответствующего конструктора производного типа.
Однако это чувствует себя неловким и громоздким. Я надеюсь, что существует более красноречивый способ сделать это. Есть ли?
Фактически, это более общая проблема, чем сериализация, называемая виртуальным конструктором
.
Традиционный подход - это Factory
, которая на основе идентификатора возвращает правильный производный тип. Есть два решения:
switch
, как вы заметили, хотя вам нужно разместить в куче
Метод прототипа выглядит следующим образом :
// 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
для вызова объекта.
РЕДАКТИРОВАТЬ: Как работает Factory?
В C ++ вы не можете создать тип, о котором вы не знаете. Идея выше заключается в том, что задача создания производного объекта
передается классу Derived
посредством метода clone
.
Далее идет Завод
. Мы собираемся использовать карту
, которая будет связывать «тег» (например, «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) );
inmemory: -------- type1 { chartype a; inttype b; }; serialize(new type1()); serialized(ignore { and ,): --------------------------- type1id,len{chartypeid,adata,inttypeid,bdata}
Я полагаю, что в идеальном протоколе сериализации каждый непримитивный тип должен иметь префикс typeid, len. Даже если вы сериализуете один тип, который не является производным от чего-либо, вы должны добавить идентификатор типа, потому что другой конец должен знать, какой тип он получает (независимо от структуры наследования). Таким образом, вы должны упомянуть идентификаторы производных классов в сериализации, потому что логически это разные типы. Поправьте меня, если я ошибаюсь.