如何在 C++ 容器中存储不同类型的对象?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4738405/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I store objects of differing types in a C++ container?
提问by Ramsey
Is there a C++ container that I could use or build that can contain, say, int
and string
and double
types? The problem I'm facing is that whenever I try to populate, say, a map, vector or list with, say, the following:
是否有一个C ++容器,我可以使用或创建可以包含,比如说,int
和string
和double
类型?我面临的问题是,每当我尝试使用以下内容填充地图、向量或列表时:
int x;
string y;
double z;
I'm restricted with the format:
我受到以下格式的限制:
list<int> mycountainer;
vector<string> mycontainer;
which forces mycontainer
to only consist of one type.
这迫使mycontainer
只包含一种类型。
Before anyone suggest generics, that wouldn't work either since the standard vector
and list
containers that come with C++ are already generic- they can be container for any types but cannot contain multiple types.
在有人建议泛型之前,这也行不通,因为C++ 附带的标准vector
和list
容器已经是泛型的——它们可以是任何类型的容器,但不能包含多种类型。
I would like to avoid using Boost also if at all possible - I'd prefer it if there is a simple way I could code this myself.
如果可能的话,我也想避免使用 Boost - 如果有一种简单的方法可以自己编写代码,我会更喜欢它。
[edit] Hey guy, many thanks for your suggestions - I should explain how I'll use this container, but it's a tad complicated hence the (big) simplification above. I think the best option from here is using the Boost. Thanks again.
[编辑] 嘿家伙,非常感谢您的建议 - 我应该解释我将如何使用这个容器,但它有点复杂,因此上面的(大)简化。我认为这里最好的选择是使用 Boost。再次感谢。
采纳答案by Ramsey
You could use (or re-implement) boost::any
and store instances of boost::any
in a container. That would be the safest, since boost::any
has probably dealt with much of the edge cases and complexity involved in solving this kind of problem in the general case.
您可以在容器中使用(或重新实现)boost::any
和存储实例boost::any
。那将是最安全的,因为boost::any
在一般情况下可能已经处理了解决此类问题所涉及的许多边缘情况和复杂性。
If you want to do something quick and dirty, create a structure or perhaps a union containing members of all potential types along with an enumeration or other indicator of which type is 'active' in the object. Be especially careful with unions as they have some interesting properties (such as invoking undefined behavior if you read the wrong union member, only one of the members can be 'active' at a time, the one that was most recently written to).
如果您想做一些快速而肮脏的事情,请创建一个结构或一个联合,其中包含所有潜在类型的成员以及对象中哪种类型是“活动的”的枚举或其他指示符。使用联合时要特别小心,因为它们有一些有趣的属性(例如,如果您读取了错误的联合成员,则会调用未定义的行为,一次只能“活动”一个成员,即最近写入的成员)。
I'm curious what you're doing that you need such a construct, though.
不过,我很好奇你在做什么,你需要这样一个构造。
回答by sbi
Well, the first question would be: Why do you think you need to store objects of different, totally unrelated types in the same container?That seems fishy to me.
那么,第一个问题是:为什么您认为需要在同一个容器中存储不同类型、完全不相关类型的对象?这对我来说似乎很可疑。
If I had the need, I'd look into boost::variant
or boost::any
.
如果我有需要,我会调查boost::variant
或boost::any
。
回答by T.E.D.
What you want is called a "hetrogenious container". C++ doesn't technically support them in the STL, but Boost does.
您想要的称为“异质容器”。C++ 在 STL 中在技术上不支持它们,但 Boost 支持。
Given that, I think you'll find your answer in this question: how-do-you-make-a-heterogeneous-boostmap
鉴于此,我认为您会在这个问题中找到答案:how-do-you-make-a-heterogeneous-boostmap
回答by ssmir
You can use either structures, or classes or std::pair.
您可以使用结构、类或 std::pair。
[edit]
[编辑]
For classes and structs:
对于类和结构:
struct XYZ {
int x;
string y;
double z;
};
std::vector<XYZ> container;
XYZ el;
el.x = 10;
el.y = "asd";
el.z = 1.123;
container.push_back(el);
For std::pair:
对于 std::pair:
#include <pair>
typedef std::pair<int, std::pair<string, double> > XYZ;
std::vector<XYZ> container;
container.push_back(std::make_pair(10, std::make_pair("asd", 1111.222)));
回答by James
You could use a struct that contains all three.
您可以使用包含所有三个的结构。
struct Data
{
int intVal;
std::string stringVal;
double doubleVal;
};
Then you could just declare list mycontainer<Data>
and use the appropriate value, provided you know what the value type is. If not, add an addition field to the struct that tells you which of the three data types is in use.
然后您可以声明list mycontainer<Data>
并使用适当的值,前提是您知道值类型是什么。如果没有,请向结构中添加一个附加字段,告诉您正在使用三种数据类型中的哪一种。
struct Data
{
enum DATATYPE { DT_INT, DT_STRING, DT_DOUBLE } type;
int intVal;
std::string stringVal;
double doubleVal;
};
If you're worried about memory usage, you could probably use a union, though I tend to avoid using them. It might be needless paranoia on my part though.
如果您担心内存使用情况,您可能可以使用联合,但我倾向于避免使用它们。不过,这可能是我不必要的偏执。
回答by jcvandan
If you have a finite number of items you need to store, put them in a class or structure.
如果您需要存储的项目数量有限,请将它们放在一个类或结构中。
If there is no limit to the items you would need to store in this container then look at a different way of doing things because the only way of doing it is by storing them as an object, and then casting them to their own type when you need to access them.
如果您需要在此容器中存储的项目没有限制,那么请查看不同的做事方式,因为唯一的方法是将它们存储为一个对象,然后在您将它们转换为自己的类型时需要访问它们。
However, if any item could potentially be in the container, then you have no way of knowing what type specific items in the container are, and therefore will not be able to cast them.
但是,如果容器中可能有任何项目,则您无法知道容器中的特定项目是什么类型,因此将无法投射它们。
If C++ contained reflection, there would possibly be a way to do this, but C++ doesn't have reflection.
如果 C++ 包含反射,则可能有一种方法可以做到这一点,但 C++ 没有反射。
回答by Rob Kennedy
The simplest method is of course to define a struct or class that has members of each of the types you wish to store. Josh's answersuggests Boost.Any, which will hold pretty much anything. If you want to restrictvalues to only those of types int
, double
, and std::string
, then the better choice would be Boost.Variant.
最简单的方法当然是定义一个结构或类,该结构或类具有您希望存储的每种类型的成员。Josh 的回答建议Boost.Any,它几乎可以容纳任何东西。如果你想限制值,只有那些类型int
,double
和std::string
,然后更好的选择将是Boost.Variant。
If you simply don't wantto use Boost, then I suggest you get over your hang-ups and use it anyway. "Not Invented Here" is a self-destructive policy. But if you can'tuse Boost, then you can write your own variant class instead. Andrei Alexandrescu wrote a three-part series on that (part 1, part 2, part 3) a few years ago, and its design inspired the one Boost uses.
如果您只是不想使用 Boost,那么我建议您克服障碍并无论如何都使用它。“不是在这里发明的”是一种自我毁灭的政策。但是如果您不能使用 Boost,那么您可以编写自己的变体类。几年前,Andrei Alexandrescu 写了一个由三部分组成的系列文章(第 1 部分、第 2部分、第 3 部分),其设计启发了 Boost 的使用。
回答by Eric Petersen
What I have for this question is not what I hoped would work. By what I think that you would like, is a container that stores multiple value types, that you can access at will.
我对这个问题的看法不是我希望的。我认为您想要的是一个存储多种值类型的容器,您可以随意访问它。
However, as such, a container would have to specify what value it holds, so you could have a class with 500 data types in it, with a correlating constructor for each data type, however, that would be super memory inefficient.
但是,因此,容器必须指定它保存的值,因此您可以拥有一个包含 500 种数据类型的类,每个数据类型都有一个相关的构造函数,但是,这将是超级内存效率低下的。
Here is my proposed suggestion, I have worked on for a day, And I hope it meets your criteria:
这是我提出的建议,我已经工作了一天,希望它符合您的标准:
#include <iostream>
#include <vector>
using namespace std;
enum class type: unsigned int {int_t, unsigned_int_t, string_t, double_t, float_t, bool_t, unipointer_t, vector_int_t, vector_unipointer_t};//just add item types here and in the switch statement to hold more void_ps in unipointer...
class unipointer {
void* obj;//the pointer to the data. any type of pointer.
type objtype;//the object type, kept as an enum class.
struct void_p {//template magic... ;D
void* void_ptr;
template<typename T>//when object is initialized, it converts the the void* pointer to the output value.
operator T() {
return reinterpret_cast<T&>(void_ptr);
}
void_p(void* val): void_ptr(val) {};
};
public:
unipointer(void_p ptr, type ptrtype) : obj(ptr), objtype(ptrtype) {}
type get_type(void) {//Once you call this function, you know the type of data stored, and can call other functions accordingly.
return objtype;
}
template<typename T>//With a temlate, get any value through a pointer to it.
T get_ptr(void){
return reinterpret_cast<T&>(obj);
}
template<typename T>//With a temlate, get any value, as an object
T get_object(void) {
return *get_ptr<T*>();
}
void_p get_auto_pointer(void) {//get any pointer to value, can't be assigned to "auto*"!
return unipointer::void_p(obj);
}
void_p get_auto_object(void) {//get any value, can't be assigned to "auto"!
return *(void_p*)get_auto_pointer();
}
};
void process_stuff(unipointer& thing, unsigned int num_of_tabs);
int main() {
double initialization = 1.2345;
float even_another = 3.14159f;
unipointer items(new vector<unipointer>{//one thicc object instance
//Initialization examles:
unipointer(new int(-12345), type::int_t),
unipointer(new unsigned int(4'294'967'295), type::unsigned_int_t),
unipointer(new string("That is how I store my items."), type::string_t),
unipointer(&initialization, type::double_t),
unipointer(&even_another, type::float_t),
unipointer(new bool(1), type::bool_t),
unipointer(new unipointer(new unipointer(new unipointer(new string("OMG! NESTING!"), type::string_t), type::unipointer_t), type::unipointer_t), type::unipointer_t),
unipointer(new vector<int>{ 1,2,3 }, type::vector_int_t),
unipointer(new vector<unipointer>{
unipointer(new string("That is how I store my nested items."), type::string_t),
unipointer(new vector<int>{4,5,6}, type::vector_int_t),
unipointer(new string("Is your head brimming with ideas yet?"), type::string_t)
} , type::vector_unipointer_t)
}, type::vector_unipointer_t);
cout << "What is in the \"items\" unipointer:" << endl;
process_stuff(items, 1);
system("pause");
}
void process_stuff(unipointer& thing, unsigned int num_of_tabs) {
//declare variables & lamda for interpretaion methods, using variable assignment with "get auto object/pointer"
unsigned int* test = 0;
double test_2 = 0;
auto tab_to_current = [num_of_tabs]() {
for (unsigned int i = 0; i < num_of_tabs; ++i) {
cout << "\t";
}
};
//format the thing.
tab_to_current();
//look through and do stuff
switch (thing.get_type()) {//just add item types here and in the enum class to hold more void_ps in unipointer...
case type::int_t:
cout << "The integer: " << *thing.get_ptr<int*>() << "." << endl;//one way of getting object back from class
break;
case type::string_t:
cout << "The string: \"" << thing.get_object<string>() << "\"." << endl;//another way
break;
case type::unsigned_int_t:
test = thing.get_auto_pointer();//another way
cout << "The unsigned integer: " << *test << "." << endl;//don't forget to de-reference it!
delete test;
break;
case type::double_t:
test_2 = thing.get_auto_object();
cout << "The double: " << test_2 << "." << endl;//even another way!
break;
case type::float_t:
cout << "The float: " << float(thing.get_auto_object()) << "." << endl;//even another way!
break;
case type::bool_t:
cout << "The boolean: " << *(bool*)thing.get_auto_pointer() << "." << endl;//even another way!
break;
case type::unipointer_t:
cout << "A unipointer, and in it:" << endl;
process_stuff(*&thing.get_object<unipointer>(), num_of_tabs+1);
tab_to_current();
cout << "[End of unipointer]" << endl;
break;
case type::vector_int_t:
cout << "A vector of integers, and in it:" << endl;
for (unsigned int i = 0; i < thing.get_object<vector<int>>().size(); ++i) {
tab_to_current();
cout << "\tItem " << i << ": " << thing.get_object<vector<int>>().at(i) << endl;
}
tab_to_current();
cout << "[End of vector of integers]" << endl;
break;
case type::vector_unipointer_t:
cout << "A vector of unipointers, and in it:" << endl;
for (unsigned int i = 0; i < thing.get_object<vector<unipointer>>().size(); ++i) {
process_stuff(*&thing.get_object<vector<unipointer>>().at(i), num_of_tabs + 1);
}
tab_to_current();
cout << "[End of unipointer vector]" << endl;
break;
}
}
The "unipointer" class should be initialized with a pointer to any object type, and also the type of object. The class can return, through a function, your data, although it is not very safe, and could be called with the wrong type of data.
“unipointer”类应该用指向任何对象类型以及对象类型的指针进行初始化。该类可以通过函数返回您的数据,尽管它不是很安全,并且可以使用错误类型的数据调用。
This is just an example of what could work, I sure hope that you take inspiration from it.
这只是一个可行的例子,我当然希望你能从中获得灵感。
And, to answer your original question, you would set up a list, or vector with the following format:
并且,要回答您的原始问题,您将使用以下格式设置一个列表或向量:
vector/list:
|
|unipointer(*double)
|
|unipointer(*int)
|
|unipointer(*string)
|
...
|
end
PS: I am a beginner with objects and templates, so this might be messy. Many apoligies.
PS:我是对象和模板的初学者,所以这可能会很混乱。许多 apolicies。