关于c ++:我应该存储整个对象,还是指向容器中对象的指针?

Should I store entire objects, or pointers to objects in containers?

从头开始设计新系统。我将使用STL存储某些长寿命对象的列表和地图。

问题:我应该确保我的对象具有复制构造函数并将对象的副本存储在STL容器中,还是通常更好地管理生命和范围并将指向这些对象的指针存储在STL容器中?

我意识到这在细节上有点简短,但我正在寻找"理论上"的更好答案,如果它存在的话,因为我知道这两种解决方案都是可能的。

使用指针有两个非常明显的缺点:1)我必须自己在STL之外的范围内管理这些对象的分配/解除分配。2)无法在堆栈上创建临时对象并将其添加到容器中。

我还缺什么吗?


因为人们都在努力提高使用指针的效率。

如果您考虑使用std::vector,并且更新很少,并且您经常重复您的集合,而且它是一种存储对象的非多态类型,那么"copies"将更有效,因为您将获得更好的引用位置。

如果更新是常见的,存储指针将节省复制/重定位成本。


这完全取决于你的情况。

如果您的对象很小,并且复制对象是轻量级的,那么在我看来,将数据存储在STL容器中非常简单,而且更容易管理,因为您不必担心终身管理。

如果您的对象很大,并且有一个默认的构造函数是没有意义的,或者对象的副本是昂贵的,那么用指针存储可能是一种方法。

如果决定使用指向对象的指针,请查看Boost指针容器库。这个Boost库包装了所有STL容器,以便与动态分配的对象一起使用。

每个指针容器(例如ptr_vector)在添加到容器中时都拥有对象的所有权,并为您管理这些对象的生存期。您还可以通过引用访问ptr_u容器中的所有元素。这样可以让你做一些事情

1
2
3
4
5
6
7
8
9
10
class BigExpensive { ... }

// create a pointer vector
ptr_vector<BigExpensive> bigVector;
bigVector.push_back( new BigExpensive("Lexus", 57700 ) );
bigVector.push_back( new BigExpensive("House", 15000000 );

// get a reference to the first element
MyClass& expensiveItem = bigList[0];
expensiveItem.sell();

这些类封装STL容器并与所有的STL算法一起工作,这是非常方便的。

也有用于将容器中指针的所有权转移到调用方(通过大多数容器中的释放函数)的设施。


如果要存储多目对象,则始终需要使用基类指针的集合。

也就是说,如果计划在集合中存储不同的派生类型,则必须存储指针或被切片牧师吃掉。


对不起,在3年后的事件,但在这里提醒注意…

在我的最后一个大项目中,我的中心数据结构是一组相当简单的对象。大约一年后,随着需求的发展,我意识到这个对象实际上需要是多态的。花了几个星期的艰难和讨厌的脑部手术来修复数据结构,使之成为一组基本类指针,并处理对象存储、投射等过程中的所有附带损伤。我花了几个月的时间让自己相信新代码正在工作。顺便说一句,这让我对C++设计的对象模型有多好的思考。

在我当前的大项目中,我的中心数据结构是一组相当简单的对象。在项目进行了大约一年(刚好是今天),我意识到这个对象实际上需要是多态的。回到网络上,找到了这个线程,找到了尼克到Boost指针容器库的链接。这正是我上一次为解决所有问题而写的东西,所以这次我将尝试一下。

不管怎样,对我来说,这是一个道义:如果你的技术规格不是100%的石头铸成的,那就去寻找指针吧,以后你可能会节省很多工作。


为什么不两全其美呢:做一个智能指针容器(如boost::shared_ptrstd::shared_ptr)。您不必管理内存,也不必处理大型复制操作。


通常,将对象直接存储在STL容器中是最好的,因为它最简单、最有效,并且最容易使用对象。

如果对象本身具有不可复制的语法或是抽象的基类型,则需要存储指针(最简单的方法是使用共享指针)


使用指针将更有效,因为容器将只复制周围的指针而不是完整对象。

这里有一些关于STL容器和智能指针的有用信息:

为什么在标准容器中使用std::auto_ptr<>是错误的?


你似乎很清楚两者的区别。如果对象很小且易于复制,则无论如何都要存储它们。

如果不是,我会考虑将智能指针(不是自动指针,一个引用计数智能指针)存储到您在堆上分配的指针上。显然,如果您选择智能指针,那么就不能存储临时堆栈分配的对象(如您所说)。

@ Torbj?RN对切片很有帮助。


如果要在代码中的其他地方引用这些对象,请将其存储在boost::shared_ptr的向量中。这样可以确保指向对象的指针在调整向量大小时仍然有效。

Ie:

1
2
3
std::vector<boost::shared_ptr<protocol> > protocols;
...
connection c(protocols[0].get()); // pointer to protocol stays valid even if resized

如果没有其他人存储指向对象的指针,或者列表没有增长和收缩,那么只存储为普通的旧对象:

1
2
std::vector<protocol> protocols;
connection c(protocols[0]); // value-semantics, takes a copy of the protocol

这个问题困扰了我一段时间。

我倾向于存储指针,但我有一些可能不适用于您的附加需求(swig-lua包装器)。

这篇文章中最重要的一点是用你的对象来测试它

我今天做这个是为了测试在1000万个对象集合上调用成员函数的速度,500次。

函数根据xdir和ydir(所有浮点成员变量)更新x和y。

我使用std::list来保存这两种类型的对象,我发现在列表中存储对象比使用指针要快一些。另一方面,性能非常接近,所以这取决于它们在应用程序中的使用方式。

作为参考,在硬件上使用-o3时,指针需要41秒才能完成,而原始对象需要30秒才能完成。