我应该将整个对象或者指向对象的指针存储在容器中吗?
从头开始设计新系统。我将使用STL存储某些长期存在的对象的列表和地图。
问:应该确保我的对象具有副本构造函数并将对象的副本存储在我的STL容器中,还是通常更好地自己管理生命和作用域,而仅将指向这些对象的指针存储在我的STL容器中?
我意识到这在细节上有些欠缺,但是我正在寻找"理论上"更好的答案(如果存在),因为我知道这两种解决方案都是可行的。
使用指针有两个非常明显的缺点:
1)我必须自己在STL之外的范围内管理这些对象的分配/取消分配。
2)我不能在堆栈上创建一个临时对象并将其添加到我的容器中。
还有什么我想念的吗?
解决方案
如果要存储多态对象,则始终需要使用基类指针的集合。
也就是说,如果我们打算在集合中存储其他派生类型,则必须存储指针或者被切片守护进程吞噬。
我们似乎对差异有很好的了解。如果对象很小且易于复制,则一定要存储它们。
如果没有,我会考虑将智能指针(不是auto_ptr,即计数智能指针的引用)存储到我们在堆上分配的指针。显然,如果选择智能指针,则无法存储临时堆栈分配的对象(如我们所说)。
@Torbj?rn很好地说明了切片。
使用指针将更加有效,因为容器将仅在周围复制指针,而不是完整对象。
这里有一些有关STL容器和智能指针的有用信息:
为什么在标准容器中使用std :: auto_ptr <>是错误的?
为什么不充分利用这两个方面的优势:做一个智能指针容器(例如boost :: shared_ptr
或者std :: shared_ptr
)。我们不必管理内存,也不必处理大型复制操作。
通常,将对象直接存储在STL容器中是最好的,因为它最简单,最有效并且最容易使用对象。
如果对象本身具有不可复制的语法或者是抽象的基本类型,则需要存储指针(最简单的方法是使用shared_ptr)
既然人们开始关注使用指针的效率。
如果我们正在考虑使用std :: vector,并且更新次数很少,并且我们经常对集合进行迭代,并且它是一种非多态类型,则存储对象"副本"会更有效,因为我们将获得更好的引用位置。
Otoh,如果更新是常见的,存储指针将节省复制/重定位成本。
这真的取决于情况。
如果对象很小,并且进行对象的复制是轻量级的,那么在我看来,将数据存储在stl容器中非常简单,并且更易于管理,因为我们不必担心寿命管理。
如果对象很大,并且没有默认的构造函数,或者对象的副本很昂贵,那么使用指针存储可能是可行的方法。
如果决定使用指向对象的指针,请查看Boost指针容器库。这个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算法,这确实非常方便。
还有一些用于将容器中指针的所有权转移到调用者的功能(通过大多数容器中的释放功能)。
如果要在代码的其他位置引用这些对象,则将其存储在boost :: shared_ptr的向量中。这样可以确保如果调整向量的大小,则指向该对象的指针将保持有效。
IE:
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