从头开始设计新系统。我将使用STL存储某些长寿命对象的列表和地图。
问题:我是否应该确保我的对象具有复制构造函数并在我的STL容器中存储对象的副本,或者通常更好地管理生命和对象。范围我自己只是将指针存储到我的STL容器中的那些对象?
我意识到这在细节上有点短暂,但我正在寻找“理论上”更好的答案,如果它存在,因为我知道这两种解决方案都是可能的。
使用指针的两个非常明显的缺点: 1)我必须在超出STL的范围内管理这些对象的分配/释放。 2)我无法在堆栈上创建临时对象并将其添加到我的容器中。
还有什么我想念的吗?
答案 0 :(得分:67)
因为人们正在考虑使用指针的效率。
如果您正在考虑使用std :: vector并且更新很少并且您经常迭代您的集合并且它是非多态类型存储对象“副本”将更有效,因为您将获得更好的参考局部性。
Otoh,如果更新是常见的存储指针将节省复制/重定位成本。
答案 1 :(得分:46)
这实际上取决于你的情况。
如果您的对象很小,并且对象的副本是轻量级的,那么在我看来将数据存储在stl容器中是简单易行的,因为您不必担心终身管理。
如果对象很大,并且默认构造函数没有意义,或者对象副本很昂贵,那么使用指针存储可能就好了。
如果您决定使用指向对象的指针,请查看Boost Pointer Container Library。此boost库包装了所有STL容器,以便与动态分配的对象一起使用。
每个指针容器(例如ptr_vector)在将对象添加到容器时获取对象的所有权,并为您管理这些对象的生命周期。您还可以通过引用访问ptr_容器中的所有元素。这可以让你做像
这样的事情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算法,这非常方便。
还有一些工具可以将容器中指针的所有权转移给调用者(通过大多数容器中的释放功能)。
答案 2 :(得分:38)
如果您要存储polymporhic对象,则始终需要使用一组基类指针。
即如果您计划在集合中存储不同的派生类型,您必须存储指针或被切片守护程序吃掉。
答案 3 :(得分:22)
很抱歉在活动结束后的3年内跳了起来,但这里有一个警示...
在我的上一个大项目中,我的中心数据结构是一组相当简单的对象。大约一年的项目,随着需求的发展,我意识到对象实际上需要是多态的。经过几周的困难和讨厌的脑外科手术将数据结构修复为一组基类指针,并处理对象存储,转换等所有附带损害。我花了几个月的时间才说服自己新代码正在运行。顺便说一下,这让我想到了C ++的对象模型设计得很好。
在我目前的大项目中,我的中心数据结构是一组相当简单的对象。大约一年的项目(恰好是今天),我意识到对象实际上需要是多态的。回到网上,找到了这个帖子,并找到了Nick的Boost指针容器库的链接。这正是我上次为了解决所有问题而必须写的内容,所以这次我会试一试。
道德,对我来说,无论如何:如果你的规范不是100%一成不变,那就去指点,你可能会在以后节省很多工作。
答案 4 :(得分:18)
为什么不充分利用这两个方面:做一个智能指针容器(例如boost::shared_ptr
或std::shared_ptr
)。您不必管理内存,也不必处理大型复制操作。
答案 5 :(得分:11)
通常将对象直接存储在STL容器中是最好的,因为它最简单,最有效,并且最容易使用该对象。
如果您的对象本身具有不可复制的语法或者是抽象基类型,则需要存储指针(最简单的方法是使用shared_ptr)
答案 6 :(得分:3)
你似乎很好地掌握了这种差异。如果对象很小并且易于复制,那么无论如何都要存储它们。
如果没有,我会考虑将智能指针(不是auto_ptr,一个引用计数智能指针)存储到您在堆上分配的指针。显然,如果您选择智能指针,那么就无法存储临时堆栈分配的对象(如您所说)。
@ Torbjörn对切片提出了一个很好的观点。
答案 7 :(得分:3)
使用指针会更有效,因为容器只会复制指针而不是完整的对象。
这里有一些关于STL容器和智能指针的有用信息:
Why is it wrong to use std::auto_ptr<> with standard containers?
答案 8 :(得分:2)
如果要在代码中的其他位置引用对象,则存储在boost :: shared_ptr的向量中。这样可以确保在调整矢量大小时,指向对象的指针仍然有效。
即:
std::vector<boost::shared_ptr<protocol> > protocols;
...
connection c(protocols[0].get()); // pointer to protocol stays valid even if resized
如果没有其他人存储指向对象的指针,或者列表没有增长和缩小,只需存储为普通的对象:
std::vector<protocol> protocols;
connection c(protocols[0]); // value-semantics, takes a copy of the protocol
答案 9 :(得分:1)
这个问题一直困扰着我。
我倾向于存储指针,但我有一些额外的要求(SWIG lua包装)可能不适用于你。
这篇文章中最重要的一点是自己测试,使用你的对象
我今天这样做是为了测试在一千万个对象的集合上调用成员函数的速度,500次。
该函数根据xdir和ydir(所有浮点成员变量)更新x和y。
我使用std :: list来保存两种类型的对象,我发现将对象存储在列表中比使用指针稍快一些。另一方面,性能非常接近,因此它归结为它们将如何在您的应用程序中使用。
作为参考,在我的硬件上使用-O3指针需要41秒才能完成,原始对象需要30秒才能完成。