在Java中,您可以拥有一个对象列表。您可以添加多种类型的对象,然后检索它们,检查它们的类型,并对该类型执行适当的操作 例如:(如果代码不完全正确,我会从内存中去道歉)
List<Object> list = new LinkedList<Object>();
list.add("Hello World!");
list.add(7);
list.add(true);
for (object o : list)
{
if (o instanceof int)
; // Do stuff if it's an int
else if (o instanceof String)
; // Do stuff if it's a string
else if (o instanceof boolean)
; // Do stuff if it's a boolean
}
在C ++中复制此行为的最佳方法是什么?
答案 0 :(得分:20)
boost::variant
类似于dirkgently对boost::any
的建议,但支持访问者模式,这意味着以后更容易添加特定于类型的代码。此外,它在堆栈上分配值而不是使用动态分配,从而导致代码效率稍高。
编辑:正如litb在评论中指出的那样,使用variant
代替any
意味着您只能保留一个预先指定的类型列表中的值。这通常是一种力量,尽管这可能是提问者案件中的一个弱点。
这是一个例子(虽然不使用访客模式):
#include <vector>
#include <string>
#include <boost/variant.hpp>
using namespace std;
using namespace boost;
...
vector<variant<int, string, bool> > v;
for (int i = 0; i < v.size(); ++i) {
if (int* pi = get<int>(v[i])) {
// Do stuff with *pi
} else if (string* si = get<string>(v[i])) {
// Do stuff with *si
} else if (bool* bi = get<bool>(v[i])) {
// Do stuff with *bi
}
}
(是的,你应该在技术上使用vector<T>::size_type
而不是int
代表i
的类型,无论如何你应该在技术上使用vector<T>::iterator
,但我是试图保持简单。)
答案 1 :(得分:14)
使用Boost.Variant和访问者的示例:
#include <string>
#include <list>
#include <boost/variant.hpp>
#include <boost/foreach.hpp>
using namespace std;
using namespace boost;
typedef variant<string, int, bool> object;
struct vis : public static_visitor<>
{
void operator() (string s) const { /* do string stuff */ }
void operator() (int i) const { /* do int stuff */ }
void operator() (bool b) const { /* do bool stuff */ }
};
int main()
{
list<object> List;
List.push_back("Hello World!");
List.push_back(7);
List.push_back(true);
BOOST_FOREACH (object& o, List) {
apply_visitor(vis(), o);
}
return 0;
}
使用这种技术的一个好处是,如果稍后,您将另一种类型添加到变体中,并且您忘记修改访问者以包含该类型,则它将无法编译。您拥有来支持所有可能的情况。然而,如果您使用switch或cascading if语句,很容易忘记在任何地方进行更改并引入错误。
答案 2 :(得分:13)
C ++不支持异类容器。
如果您不打算使用boost
,那么hack就是创建一个虚拟类,并让所有不同的类派生自这个虚拟类。创建一个您选择的容器来容纳虚拟类对象,然后就可以开始了。
class Dummy {
virtual void whoami() = 0;
};
class Lizard : public Dummy {
virtual void whoami() { std::cout << "I'm a lizard!\n"; }
};
class Transporter : public Dummy {
virtual void whoami() { std::cout << "I'm Jason Statham!\n"; }
};
int main() {
std::list<Dummy*> hateList;
hateList.insert(new Transporter());
hateList.insert(new Lizard());
std::for_each(hateList.begin(), hateList.end(),
std::mem_fun(&Dummy::whoami));
// yes, I'm leaking memory, but that's besides the point
}
如果您要使用boost
,可以尝试boost::any
。 Here是使用boost::any
。
您可能会感兴趣的两位领先的C ++专家发现这个优秀的article。
现在,boost::variant
是另一件需要注意的事项j_random_hacker。所以,这里有一个comparison来了解使用什么。
使用boost::variant
上面的代码看起来像这样:
class Lizard {
void whoami() { std::cout << "I'm a lizard!\n"; }
};
class Transporter {
void whoami() { std::cout << "I'm Jason Statham!\n"; }
};
int main() {
std::vector< boost::variant<Lizard, Transporter> > hateList;
hateList.push_back(Lizard());
hateList.push_back(Transporter());
std::for_each(hateList.begin(), hateList.end(), std::mem_fun(&Dummy::whoami));
}
答案 3 :(得分:12)
这种事情多久有用一次?我已经用C ++编程了很多年,在不同的项目上,从来没有真正想要一个异类容器。由于某种原因,它可能在Java中很常见(我的Java经验要少得多),但是对于Java项目中任何给定的使用,可能有一种方法可以做一些在C ++中更好的工作。
C ++比Java更重视类型安全,而且这种类型不安全。
那就是说,如果对象没有任何共同点,为什么要将它们存储在一起?
如果他们确实有共同点,你就可以为他们做一个继承的课程;或者,使用boost :: any。如果它们继承,则具有要调用的虚函数,或者使用dynamic_cast&lt;&gt;如果你真的需要。
答案 4 :(得分:3)
我只想指出,使用动态类型转换以便基于类型进行分支通常会暗示架构中的缺陷。大多数时候,您可以使用虚拟功能实现相同的效果:
class MyData
{
public:
// base classes of polymorphic types should have a virtual destructor
virtual ~MyData() {}
// hand off to protected implementation in derived classes
void DoSomething() { this->OnDoSomething(); }
protected:
// abstract, force implementation in derived classes
virtual void OnDoSomething() = 0;
};
class MyIntData : public MyData
{
protected:
// do something to int data
virtual void OnDoSomething() { ... }
private:
int data;
};
class MyComplexData : public MyData
{
protected:
// do something to Complex data
virtual void OnDoSomething() { ... }
private:
Complex data;
};
void main()
{
// alloc data objects
MyData* myData[ 2 ] =
{
new MyIntData()
, new MyComplexData()
};
// process data objects
for ( int i = 0; i < 2; ++i ) // for each data object
{
myData[ i ]->DoSomething(); // no type cast needed
}
// delete data objects
delete myData[0];
delete myData[1];
};
答案 5 :(得分:2)
遗憾的是,在C ++中没有简单的方法。您必须自己创建一个基类,并从该类派生所有其他类。创建一个基类指针向量,然后使用dynamic_cast(它带有自己的运行时开销)来查找实际类型。
答案 6 :(得分:2)
为了完成这个主题,我想提一下,你可以通过使用void *然后将它转换成它必须使用的纯粹C来实现这一点(好吧,我的例子不是纯C,因为它使用向量但这节省了一些代码)。如果你知道你的对象是什么类型,或者你在某个地方存储了一个记住它的字段,这将有效。你肯定不想这样做,但这里有一个例子来表明它是可能的:
#include <iostream>
#include <vector>
using namespace std;
int main() {
int a = 4;
string str = "hello";
vector<void*> list;
list.push_back( (void*) &a );
list.push_back( (void*) &str );
cout << * (int*) list[0] << "\t" << * (string*) list[1] << endl;
return 0;
}
答案 7 :(得分:2)
虽然你不能在容器中存储基本类型,但你可以创建基本类型的包装类,它类似于Java的自动装箱基元类型(在你的例子中,原始类型的文字实际上是自动装箱的);其实例出现在C ++代码中(并且可以(几乎)可以使用)就像原始变量/数据成员一样。
请参阅Object Wrappers for the Built-In Types中的Data Structures and Algorithms with Object-Oriented Design Patterns in C++。
使用包装对象,您可以使用c ++ typeid()运算符来比较类型。
我很确定以下比较可行:
if (typeid(o) == typeid(Int))
[其中Int将是int基本类型的包装类,等等...]
(否则只需向返回typeid的原始包装器添加一个函数,从而:
if (o.get_typeid() == typeid(Int))
...
话虽如此,关于你的例子,这对我有代码味道。 除非这是您检查对象类型的唯一位置, 我倾向于使用多态(特别是如果你有其他类型的方法/函数)。在这种情况下,我将使用原始包装器添加一个接口类,声明延迟方法(用于执行'do stuff'),这将由每个包装的基本类实现。有了这个你就可以使用你的容器迭代器并消除你的if语句(再次,如果你只有这种类型的比较,使用多态来设置延迟方法只是因为这将是过度杀伤)。
答案 8 :(得分:1)
我是一个相当缺乏经验的人,但这就是我要去的地方 -
我相信可以提供更好的解决方案。我也相信可以做出更好的解释。我已经了解到我有一些糟糕的C ++编程习惯,所以我试图在不进入代码的情况下传达我的想法。
我希望这会有所帮助。
答案 9 :(得分:1)
除了大多数人已经指出的事实,你不能这样做,或者更重要的是,你真的不想这样做。
让我们摒弃你的榜样,并考虑更接近现实生活中的例子。具体来说,我在一个真正的开源项目中看到了一些代码。它试图模拟字符数组中的cpu。因此,它将在数组中放入一个字节“操作码”,后跟0,1或2个字节,可以是字符,整数或指向字符串的指针,基于操作码。为了解决这个问题,它涉及了许多小小的问题。
我的简单解决方案:4个独立的堆栈&lt;&gt; s:一个用于“操作码”枚举,一个用于字符,整数和字符串。取下操作码堆栈的下一个,然后将获取其他三个中的哪一个来获取操作数。
很有可能以类似的方式处理您的实际问题。
答案 10 :(得分:0)
好吧,您可以创建一个基类,然后创建从中继承的类。然后,将它们存储在std :: vector中。
答案 11 :(得分:0)
简短的回答是......你做不到。
答案很长......你必须定义自己的新对象,这些对象都是从基础对象继承的。在Java中,所有对象最终都来自“对象”,这是允许您执行此操作的对象。
答案 12 :(得分:0)
C ++中的RTTI(运行时类型信息)一直很难,尤其是交叉编译器。
您最好的选择是使用STL并定义一个接口以确定对象类型:
public class IThing
{
virtual bool isA(const char* typeName);
}
void myFunc()
{
std::vector<IThing> things;
// ...
things.add(new FrogThing());
things.add(new LizardThing());
// ...
for (int i = 0; i < things.length(); i++)
{
IThing* pThing = things[i];
if (pThing->isA("lizard"))
{
// do this
}
// etc
}
}
麦克