你的方法基本上是朝着正确的方向发展的。你必须知道你输入的类型。您可以使用boost::any
并且可以将任何内容放入地图中,只要您知道自己放入了什么:
std::map<std::string, boost::any> table;
table["hello"] = 10;
std::cout << boost::any_cast<int>(table["hello"]); // outputs 10
一些答案建议使用boost::variant
来解决这个问题。但它不会让您在地图中存储任意类型的值(如您所愿)。您必须事先知道可能的类型集。鉴于此,您可以更轻松地执行上述操作:
typedef boost::variant<int, std::string, void*> variant_type;
std::map<std::string, variant_type> table;
table["hello"] = 10;
// outputs 10. we don't have to know the type last assigned to the variant
// but the variant keeps track of it internally.
std::cout << table["hello"];
这是有效的,因为为此目的boost::variant
重载。operator<<
重要的是要理解,如果你想保存当前包含在变体中的内容,你仍然必须知道类型,就像下面的boost::any
例子一样:
typedef boost::variant<int, std::string, void*> variant_type;
std::map<std::string, variant_type> table;
table["hello"] = "bar";
std::string value = boost::get<std::string>(table["hello"]);
变量的赋值顺序是代码控制流的运行时属性,但任何变量使用的类型都是在编译时确定的。所以如果你想从变量中获取值,你必须知道它的类型。另一种方法是使用访问,如变体文档所述。它之所以起作用,是因为该变体存储了一个代码,该代码告诉它上次分配给它的类型。基于此,它在运行时决定它使用哪个访问者的重载。boost::variant
相当大并且不完全符合标准,虽然boost::any
符合标准但即使对于小类型也使用动态内存(所以它比较慢。变体可以将堆栈用于小类型)。所以你必须权衡你使用的东西。
如果您确实想将仅在它们做某事的方式上有所不同的对象放入其中,那么多态是一种更好的方法。您可以拥有一个从中派生的基类:
std::map< std::string, boost::shared_ptr<Base> > table;
table["hello"] = boost::shared_ptr<Base>(new Apple(...));
table["hello"]->print();
这基本上需要这个类布局:
class Base {
public:
virtual ~Base() { }
// derived classes implement this:
virtual void print() = 0;
};
class Apple : public Base {
public:
virtual void print() {
// print us out.
}
};
即boost::shared_ptr
所谓的智能指针。如果您将它们从地图中删除,并且没有其他东西再引用它们,它将自动删除您的对象。理论上,您也可以使用普通指针,但使用智能指针将大大提高安全性。阅读我链接到的 shared_ptr 手册。