从 iostream 读取超类的子类实例。 >> 运算符如何知道哪个子类?
Reading subclass instance of superclass from iostream. How does >> operator know which subclass?
我有一个包含多个子class 的超级class 元素,我们称它们为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;
但这行不通,因为我不知道我要读取的元素是 Element 还是 A 或 B。(在我的应用程序中,我从未实际实例化 Element。所有对象是子 class 之一。)
我求助于写出一个字符来指示 class...
if (dynamic_cast<A>(e))
{
out << 'A';
out << e;
} else if (dynamic_cast<B>(e))
{
out << 'B';
out << e;
}
然后 switch/casing 阅读如下:
char t;
Element *pe;
...
in >> t;
switch (t)
{
case 'A':
pe = new A;
break;
case 'B':
pe = new B;
break;
}
in >> *pe;
不过好像不雅
流式传输不同对象的更好方法是什么?
从本质上讲,这就是任何序列化解决方案的归结所在。虽然优雅可能会有所改善,但使用代码生成可能仍然更好(序列化框架可以做到这一点)。
使用虚函数或映射绝对可以避免动态转换(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和工厂函数)包装在一个标准接口中。
我有一个包含多个子class 的超级class 元素,我们称它们为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;
但这行不通,因为我不知道我要读取的元素是 Element 还是 A 或 B。(在我的应用程序中,我从未实际实例化 Element。所有对象是子 class 之一。) 我求助于写出一个字符来指示 class...
if (dynamic_cast<A>(e))
{
out << 'A';
out << e;
} else if (dynamic_cast<B>(e))
{
out << 'B';
out << e;
}
然后 switch/casing 阅读如下:
char t;
Element *pe;
...
in >> t;
switch (t)
{
case 'A':
pe = new A;
break;
case 'B':
pe = new B;
break;
}
in >> *pe;
不过好像不雅
流式传输不同对象的更好方法是什么?
从本质上讲,这就是任何序列化解决方案的归结所在。虽然优雅可能会有所改善,但使用代码生成可能仍然更好(序列化框架可以做到这一点)。
使用虚函数或映射绝对可以避免动态转换(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和工厂函数)包装在一个标准接口中。