Blindy 的答案非常好(+1),但只是为了完成答案:还有另一种方法可以在没有库的情况下使用动态继承:
class MyFieldInterface
{
int m_Size; // of course use appropriate access level in the real code...
~MyFieldInterface() = default;
}
template <typename T>
class MyField : public MyFieldInterface {
T m_Value;
}
struct MyClass {
std::map<string, MyFieldInterface* > fields;
}
优点:
- 任何 C++ 编码人员都熟悉它
- 它不会强迫你使用 Boost(在某些情况下你是不允许的);
缺点:
- 您必须在堆/空闲存储上分配对象并使用引用语义而不是值语义来操作它们;
- 以这种方式公开的公共继承可能会导致过度使用动态继承以及许多与您的类型相关的长期问题确实过于相互依赖;
- 如果指针向量必须拥有对象,则指针向量是有问题的,因为您必须管理销毁;
因此,如果可以,请使用 boost::any 或 boost::variant 作为默认值,否则仅考虑此选项。
要解决最后一个缺点,您可以使用智能指针:
struct MyClass {
std::map<string, std::unique_ptr<MyFieldInterface> > fields; // or shared_ptr<> if you are sharing ownership
}
然而,还有一个潜在的更成问题的地方:
它强制您使用 new/delete(或 make_unique/shared)创建对象。这意味着实际对象是在分配器提供的任何位置(大多数是默认位置)的空闲存储(堆)中创建的。因此,由于缓存未命中,经常浏览对象列表并没有可能那么快。
如果您关心尽可能快地循环遍历此列表的性能(如果不是,请忽略以下内容),那么您最好使用 boost::variant (如果您已经知道您将使用的所有具体类型)或使用某种类型擦除的多态容器。
这个想法是容器将管理相同类型的对象数组,但仍然公开相同的接口。该接口可以是一个概念(使用鸭子类型技术)或动态接口(如我的第一个示例中的基类)。优点是容器会将相同类型的对象保存在单独的向量中,因此通过它们很快。只有从一种类型转换到另一种类型不是。
这是一个例子(图片来自那里):http ://bannalia.blogspot.fr/2014/05/fast-polymorphic-collections.html
但是,如果您需要保持插入对象的顺序,这种技术就会失去兴趣。
无论如何,有几种可能的解决方案,这在很大程度上取决于您的需求。如果您对自己的案例没有足够的经验,我建议使用我在示例中首先解释的简单解决方案或 boost::any/variant。
作为对这个答案的补充,我想指出非常好的博客文章,这些文章总结了您可以使用的所有 C++ 类型擦除技术,并附有评论和优缺点: