【问题标题】:Reading subclass instance of superclass from iostream. How does >> operator know which subclass?从 iostream 读取超类的子类实例。 >> 运算符如何知道哪个子类?
【发布时间】:2021-09-05 23:20:31
【问题描述】:

我有一个包含多个子类的超类 Element,我们称它们为 A 和 B。我想重载 >,以便保存和加载我的对象。

class Element
{
public:
   Element();
   int superProperty;
   virtual void write(iostream &out)
   {
      out << superProperty;
   }
   virtual void read(iostream &in)
   {
      in >> superProperty;
   }
};

iostream operator<<(iostream &out, const Element &elt)
{
  elt.write(out);
  return(out);
}

iostream operator>>(iostream &in Element &elt)
{
  elt.read(in);
  return(in);
}

class A : public Element
{
public:
  A();
  int subProperty;
   void write(iostream &out)
   {
      Element::write(out);
      out << subProperty;
   }
   void read(iostream &in)
   {
      Element::read(in);
      in >> subProperty;
   }
};

class B : public Element
{
public:
  B();
  double subProperty;
   void write(iostream &out)
   {
      Element::write(out);
      out << subProperty;
   }
   void read(iostream &in)
   {
      Element::read(in);
      in >> subProperty;
   }
};

有了这些定义,我可以很容易地写出我的元素文件,每个元素都写成

iostream mystream;
Element e;
...
mystream << e;

我被困的地方是把它们读回来。我希望它看起来像这样:

iostream mystream;
Element *pe;
...
pe = new Element();  // the problem is right here
mystream >> *pe;

但这行不通,因为我不知道我要读取的元素是元素还是 A 或 B。(在我的应用程序中,我从未实际实例化元素。所有对象都是一个的子类。) 我求助于写出一个字符来表示类...

if (dynamic_cast<A>(e))
{
   out << 'A';
   out << e;
} else if (dynamic_cast<B>(e))
{
  out << 'B';
  out << e;
}

然后切换/大小写如下:

    char t;
    Element *pe;
...
    in >> t;
    switch (t)
    {
      case 'A':
        pe = new A;
        break;
      case 'B':
        pe = new B;
        break;
    }
    in >> *pe;

但它似乎不优雅。

流式传输不同对象的更好方法是什么?

【问题讨论】:

  • 我认为这是一种正确的做法。但也许您可能想使用一些序列化框架,该框架可能已经提供了一种注册类型和其他功能(如版本控制)的方法。

标签: c++ iostream


【解决方案1】:

本质上,这就是任何序列化解决方案都会归结为的内容。虽然优雅可能会有所改进,但使用代码生成可能仍然更好(序列化框架可以做到这一点)。

使用虚函数或映射绝对可以避免动态转换(type_index 标记1)。开关也可以用地图(标签到工厂)替换。甚至可以(通过一些模板魔法)使用相同的代码来初始化两个地图,例如:

using Factory = void(*)();
struct SerializationInfo {
  char key;
  type_index type;
  Factory factory;
};

template <class T>
SerializationInfo Serializable(char key) // note that SerializationInfo is not a template!
{
    return {key, typeid(T), []() { return new T(); }}; // IIRC captureless lambda is convertible to a function pointer
}

Maps buildSerializationMaps(initializer_list<SerializationInfo>);

buildSerializationMaps({
    Serializable<A>('A'),
    Serializable<B>('B'),
});

其中Serializable 是一个函数模板,它将所有序列化信息(键、类型 ID 和工厂函数)包装在一个标准接口中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-28
    • 1970-01-01
    • 2012-07-27
    • 2021-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多