我正在尝试自学C ++,而我一直使用的传统“新语言”练习之一是实现一些数据结构,如二叉树或链表。在Java中,这相对简单:我可以定义一些维护实例变量Object data
的类Node,以便有人可以在列表或树的每个节点中存储任何类型的对象。 (后来我使用泛型来修改它;这不是这个问题的内容。)
我找不到类似的,惯用的C ++方式来存储“任何类型的对象”。在C中,我使用void
指针;显然,同样的事情适用于C ++,但是当我构造std::string
的实例并尝试将其存储到列表/树中时,我会遇到问题(关于从std::string&
到{的无效转换的问题{1}})。有这样的方式吗? C ++是否与Java的Object(或Objective-C的NSObject)等效?
奖金问题:如果没有,我需要继续使用无效指针,那么将void*
存储到std::string
的“正确”方法是什么?我偶然发现了void*
,但这似乎对我正在做的事情有点冗长。还有更好的方法吗?
答案 0 :(得分:12)
与Java不同,C ++没有所有对象继承的基础对象。您想要做的通常做法是使用templates。标准C ++库中的所有容器都使用此方法。
与Java不同,C ++不依赖于多态/继承来实现通用容器。在Java中,所有对象都从Object
继承,因此任何类都可以插入到带有Object
的容器中。但是,C ++模板是编译时构造,它指示编译器为您使用的每种类型实际生成不同的类。所以,例如,如果你有:
template <typename T>
class MyContainer { ... };
然后,您可以创建一个MyContainer
来获取std::string
个对象,另一个MyContainer创建int
个。
MyContainer<std::string> stringContainer;
stringContainer.insert("Blah");
MyContainer<int> intContainer;
intContainer.insert(3342);
答案 1 :(得分:9)
你可以看一下boost :: any class。它是类型安全的,你可以把它放到标准集合中,你不需要链接任何库,该类在头文件中实现。
它允许您编写如下代码:
#include <list>
#include <boost/any.hpp>
typedef std::list<boost::any> collection_type;
void foo()
{
collection_type coll;
coll.push_back(boost::any(10));
coll.push_back(boost::any("test"));
coll.push_back(boost::any(1.1));
}
完整文档在此处:http://www.boost.org/doc/libs/1_40_0/doc/html/any.html
答案 2 :(得分:3)
您要找的是模板。它们允许您创建类和函数,允许您采用任何数据类型。
答案 3 :(得分:2)
模板是执行此操作的静态方法。它们的行为类似于Java和C#泛型,但它们是100%静态的(编译时)。如果你需要在同一个容器中存储不同类型的objetcs,请使用它(其他答案很好地描述了这一点)。
但是,如果您需要在同一容器中存储不同类型的对象,则可以通过将指针存储在基类上以动态方式执行此操作。当然,您必须定义自己的对象层次结构,因为C ++中没有这样的“Object”类:
#include <list>
class Animal {
public:
virtual ~Animal() {}
};
class Dog : public Animal {
public:
virtual ~Dog() {}
};
class Cat : public Animal {
public:
virtual ~Cat() {}
};
int main() {
std::list<Animal*> l;
l.push_back(new Dog);
l.push_back(new Cat);
for (std::list<Animal*>::iterator i = l.begin(); i!= l.end(); ++i)
delete *i;
l.clear();
return 0;
}
智能指针更易于使用。 boost::smart_ptr
的示例:
std::list< boost::smart_ptr<Animal> > List;
List.push_back(boost::smart_ptr<Animal>(new Dog));
List.push_back(boost::smart_ptr<Animal>(new Cat));
List.clear(); // automatically call delete on each stored pointer
答案 4 :(得分:1)
您应该能够使用标准的C风格演员阵容将void*
投射到string*
。请记住,引用在使用时不会被视为指针,它被视为普通对象。因此,如果您通过引用函数传递值,则仍需要取消它以获取其地址。
然而,正如其他人所说,更好的方法是使用模板
答案 5 :(得分:1)
static_cast<char*>(str.c_str())
对我来说很奇怪。 str.c_str()
检索类似C的字符串,但类型为const char *
,要转换为char *
,您通常会使用const_cast<char *>(str.c_str())
。除非这样做不好,因为你要干预string
的内部。你确定你没有收到警告吗?
您应该可以使用static_cast<void *>(&str)
。您收到的错误消息告诉我您有其他错误,所以如果您可以发布代码,我们可以查看它。 (数据类型std::string&
是对string
的引用,而不是指向一个的引用,因此错误消息是正确的。我不知道你是如何获得引用而不是指针的。 )
而且,是的,这很冗长。它的目的是。在C ++程序中,Casting通常被认为是一种难闻的气味,Stroustrup希望演员阵容很容易找到。正如在其他答案中所讨论的,构建任意基类型数据结构的正确方法是使用模板,而不是强制转换和指针。