STL容器的元件寿命

时间:2012-03-14 06:04:09

标签: c++ memory-management stl

我正在尝试将对象保存在stl容器中(在本例中为vector),并希望容器在销毁时销毁对象,但我无法弄清楚细节。

我不想这样做的一种方法就是像

一样使用它
vector<MyClass> myVec;
myVec.push_back(MyClass(...));

由于这里的构造函数被调用两次(一次在上面的代码中,然后在向量中复制构造函数)和析构函数一次。

最直接的替代方法是使用指针来存储动态分配的对象,但是在向量销毁时不会调用 MyClass 的析构函数。存储 auto_ptr 而不是普通指针会在 myVec.push_back(...)时出现错误。

在让容器的析构函数调用元素的析构函数时,有没有避免第一个选项?

感谢您的回答!

修改

考虑类似的问题;如何使用抽象基类实现拥有对象的容器。唯一指针(Boost的unique_ptr)没有复制构造函数,所以不能直接使用它。

class A {};             // Abstract base class.
class B : public A {};  // Sub class.

...


vector<A *> vec;
vec.push_back(new B());

// At destruction of vec, destroy elements left in container.

5 个答案:

答案 0 :(得分:4)

尚未提及的替代方案是Boost Pointer Container Library

  

Boost.Pointer Container提供用于保存堆分配的容器   对象以异常安全的方式并且开销最小。目的   该库特别适合在C ++中使OO编程更容易   通过建立一套标准的类,方法和设计   处理OO特定问题

使用boost::ptr_vector而不是推回副本,您可以将指针推送到动态分配的对象。 ptr_vector获取这些对象的所有权,并确保在删除ptr_vector时删除它们。从ptr_vector读取的客户端使用与常规std::vector相同的接口,因此他们不必处理指针。例如,boost::ptr_vector<T>::front()会返回引用。

文档的“动机”部分将帮助您确定这是否适合您。

答案 1 :(得分:3)

C ++ 11有emplace_back,无论你将它提供给元素构造函数还是直接就地构建它

#include <vector>
#include <iostream>

struct X{
  X(int i, float f, bool b){
    std::cout << "X(" << i << ", " << f << ", " << b << ")\n";
  }
};

int main(){
  std::vector<X> vx;
  vx.emplace_back(42, 3.14f, true);
}

Live example on Ideone.

在C ++ 03中,你运气不好,不得不接受复制ctor调用(或者更确切地说,参数中有两个 - 一个进入向量的内部数组)。如果你的课程被正确地设计,那应该只是一个小的效率不便。

答案 2 :(得分:2)

最好的方法是在标准库容器中按值使用元素 标准库容器适用于值语义。即:它们按值存储元素,并且您确定容器获取元素的所有权。因此,您不需要明确的手动内存管理。

只要你遵守 Rule of Three ,在按值存储元素时调用复制构造函数和析构函数对你来说应该不是问题。

如果指针是容器元素,则必须手动管理内存并显式取消分配动态分配 在这种情况下,您需要将对象驻留在动态内存中,您应该使用 Smart pointers
不推荐使用auto_ptr,它不能在标准库容器中使用,因为它具有非直观的赋值行为。 unique_ptr是新c ++ 11标准提出的auto_ptr的最佳选择。

请注意,使用哪个智能指针取决于元素的生命周期所有权语义,请查看链接以了解如何选择一个供您使用的链接。

答案 3 :(得分:1)

如果你想存储我喜欢boost::ptr_vector

的指针

这就像vector一样,但是存储并拥有指针的所有权。

boost::ptr_vector<X>优于std::vector<some_smart_ptr<X>>的优点是ptr_vector上的元素访问返回对对象的引用,而不是对(智能)指针的引用。这使得使用标准算法的容器更容易(因为您不需要绑定仿函数来取消引用元素)。

但除非有一个很好的理由,否则最好按正常std::vector按值存储对象。

有充分理由可能包括:

  • 复制非常昂贵。
  • 无法使用完美转发构建到位。
  • 容器必须存储多态对象。

答案 4 :(得分:0)

听起来你想要一个具有相当于包含vector<T>的生命周期的值。如果是这样,这是一个考虑使用std::shared_ptr<T>的好地方,这是一个引用计数指针类型

typedef std::shared_ptr<MyClass> MyClassPtr;
...
vector<MyClassPtr> myVec;
myVec.push_back(new MyClass(...));