如何从序列化数据中"序列化"派生类?

Joh*_*cha 9 c++ serialization

如何从序列化数据中"序列化"派生类?或许我应该说,有没有更好的方法将数据"反序列化"为派生类?

例如,假设你有一个纯虚基类(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)
}
Run Code Online (Sandbox Code Playgroud)

因此,在学习派生对象类型之后,我们调用适当的派生类型构造函数.

然而,这感觉很尴尬和麻烦.我希望有一种更有说服力的方式来做到这一点.在那儿?

Mat*_* M. 3

事实上,这是一个比序列化更普遍的问题,称为Virtual Constructor.

传统的方法是 a Factory,它基于 ID 返回正确的派生类型。有两种解决方案:

  • 您注意到的方法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;
};
Run Code Online (Sandbox Code Playgroud)

将其设置为单例有点传统Factory,但这完全是另一回事。

对于正确的反序列化,如果您有一个虚拟方法deserialize来调用对象,那就更容易了。

编辑:工厂如何运作?

在 C++ 中,您无法创建您不了解的类型。因此,上面的想法是通过方法将构建Derived对象的任务交给类。Derivedclone

接下来是Factory. 我们将使用 amap它将“标签”(例如"Derived")与对象的实例相关联(Derived在这里说)。

Factory factory;
Derived derived;
factory.set("Derived", &derived);
Run Code Online (Sandbox Code Playgroud)

现在,当我们想要创建一个在编译时不知道类型的对象时(因为类型是动态决定的),我们将一个标签传递给工厂并要求返回一个对象。

std::unique_ptr<Base> base = factory.get("Derived");
Run Code Online (Sandbox Code Playgroud)

在幕后,Factory将找到Base*与标签关联的对象"Derived"并调用clone该对象的方法。这实际上(在这里)创建一个运行时类型的对象Derived

我们可以使用typeid运算符来验证这一点:

assert( typeid(base) == typeid(Derived) );
Run Code Online (Sandbox Code Playgroud)