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%的石头铸成的,那就去寻找指针吧,以后你可能会节省很多工作。
为什么不两全其美呢:做一个智能指针容器(如
通常,将对象直接存储在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秒才能完成。