我应该将整个对象或者指向对象的指针存储在容器中吗?

时间:2020-03-06 14:48:05  来源:igfitidea点击:

从头开始设计新系统。我将使用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