如何删除stl容器?

时间:2008-12-20 06:30:17

标签: c++ memory-management stl vector containers

如果在堆中创建了像vector中的vector这样的容器对象,它们是如何被销毁的?

修改

如果容器包含指针,那么如何销毁这些指针对象

12 个答案:

答案 0 :(得分:19)

指针的STL容器不会清理指向的数据。它只会清理持有指针的空间。如果您希望向量清理指针数据,则需要使用某种智能指针实现:

{
    std::vector<SomeClass*> v1;
    v1.push_back(new SomeClass());

    std::vector<boost::shared_ptr<SomeClass> > v2;
    boost::shared_ptr<SomeClass> obj(new SomeClass);
    v2.push_back(obj);
}

当该范围结束时,两个向量将释放其内部数组。 v1将泄漏创建的SomeClass,因为只有指向它的指针才在数组中。 v2不会泄漏任何数据。

答案 1 :(得分:5)

如果你有vector<T*>,你的代码需要在删除向量之前删除那些指针:否则,该内存被泄露。

知道C ++不做垃圾收集,这是一个原因的例子(语法错误的应用,自从我编写C ++以来已经有一段时间了):

typedef vector<T*> vt;
⋮
vt *vt1 = new vt, *vt2 = new vt;
T* t = new T;
vt1.push_back(t);
vt2.push_back(t);
⋮
delete vt1;

最后一行(delete vt1;)显然不应该删除它包含的指针;毕竟,它也在vt2中。所以它没有。删除vt2

也不会

(如果你想要一个在destroy上删除指针的矢量类型,当然可以编写这样的类型。可能已经写过。但要注意删除其他人仍然持有副本的指针。)

答案 2 :(得分:2)

当向量超出范围时,编译器会对其析构函数发出一个调用,从而释放堆上已分配的内存。

答案 3 :(得分:2)

这有点用词不当。与大多数STL容器一样,向量由2个逻辑部分组成。

  1. 矢量实例
  2. 实际的底层数组实现
  3. 虽然可配置,但#2几乎总是存在于堆上。但是#1可以存在于堆栈或堆中,它只取决于它的分配方式。例如

    void foo() { 
      vector<int> v;
      v.push_back(42);
    }
    

    在这种情况下,#1部分存在于堆栈中。

    现在#2如何被摧毁?当矢量的第一部分被破坏时,它也将破坏第二部分。这是通过删除vector类的析构函数中的底层数组来完成的。

答案 4 :(得分:2)

如果将指针存储在STL容器类中,则需要在对象被销毁之前手动删除它们。这可以通过循环整个容器并删除每个项目,或使用某种智能指针类来完成。但是,请不要使用auto_ptr,因为根本不能使用容器。

这样做的一个好的副作用是你可以在你的程序中保留多个指针容器,但只有那些容器拥有这些容器,你只需要清理那个容器。

删除指针的最简单方法是:

for (ContainerType::iterator it(container.begin()); it != container.end(); ++it)
{
    delete (*it);
}

答案 5 :(得分:2)

在向量中使用智能指针,或使用boost的ptr_vector。它会自动释放其内部分配的对象。还有地图,集等等。

http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/ptr_vector.html 和主要网站: http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/ptr_container.html

答案 6 :(得分:0)

与堆中的任何其他对象一样,必须手动销毁它(使用delete)。

答案 7 :(得分:0)

回答你的第一个问题:

STL课程没什么特别的(我希望)。它们的功能与其他模板类完全相同。因此,如果在堆上分配它们,它们不会被自动销毁,因为C ++上没有垃圾收集(除非你告诉它与一些花哨的autoptr业务或其他东西)。如果你在堆栈上分配它(没有新的),它很可能会自动由C ++管理。

关于第二个问题,这是一个非常简单的ArrayOfTen类,用于演示C ++中典型内存管理的基础知识:

/* Holds ten Objects. */
class ArrayOfTen {
    public:
        ArrayOfTen() {
            m_data = new Object[10];
        }

        ~ArrayOfTen() {
            delete[] m_data;
        }

        Object &operator[](int index) {
            /* TODO Range checking */
            return m_data[index];
        }

    private:
        Object *m_data;

        ArrayOfTen &operator=(const ArrayOfTen &) { }
};

ArrayOfTen myArray;
myArray[0] = Object("hello world"); // bleh

基本上,ArrayOfTen类在堆上保留十个Object元素的内部数组。当在构造函数中调用new []时,在堆上分配十个对象的空间,并构造十个对象。 Simiarly,当在析构函数中调用delete []时,十个对象被解构,然后释放先前分配的内存。

对于大多数(所有?)STL类型,调整大小是在幕后完成的,以确保有足够的内存设置以适应您的元素。上面的类只支持十个对象的数组。它基本上是Object [10]的一个非常有限的typedef。

答案 8 :(得分:0)

要删除指向的元素,我写了一个简单的仿函数:

template<typename T>
struct Delete {
    void operator()( T* p ) const { delete p; }
};

std::vector< MyType > v;
// ....
std::for_each( v.begin(), v.end(), Delete<MyType>() );

但是当向量的内容是......嗯......共享时,你应该回退共享指针。是。

答案 9 :(得分:0)

答案 10 :(得分:0)

标准STL容器使用复制构造函数将原始对象的副本放入容器中。当容器被销毁时,也会调用容器中每个对象的析构函数来安全地销毁对象。

指针的处理方式相同。
事情是指针是POD数据。指针的复制构造函数只是复制地址而POD数据没有析构函数。如果您希望容器管理指针,您需要:

  • 使用智能指针容器。 (例如共享指针)。
  • 使用boost ptr容器。

我更喜欢指针容器:
指针容器与STL容器相同,除了你将指针放入它们,但容器然后取得指针指向的对象的所有权,因此当容器被销毁时将释放对象(通常通过调用delete)。

当您访问ptr容器的成员时,它们通过引用返回,因此它们的行为就像标准容器中使用的标准容器一样。

int main()
{
    boost::ptr_vector<int>    data;

    data.push_back(new int(5));
    data.push_back(new int(6));

    std::cout << data[0] << "\n";  // Prints 5.
    std::cout << data[1] << "\n";  // Prints 6.


}   // data deallocated.
    // This will also de-allocate all pointers that it contains.
    // by calling delete on the pointers. Therefore this will not leak.

还应该指出容器中的智能指针是一种有效的替代方法,不幸的是std :: auto_ptr&lt;&gt;对于这种情况,它不是智能指针的有效选择。

这是因为STL容器假设它们包含的对象是可复制的,不幸的是std :: auto_ptr&lt;&gt;在传统意义上是不可复制的,因为它会破坏副本上的原始值,因此副本的来源不能是常量。

答案 11 :(得分:0)

STL容器就像任何其他对象一样,如果你实例化它在堆栈上创建的那个:

std::vector<int> vec(10);

就像任何其他堆栈变量一样,它只存在于其定义的函数范围内,不需要手动删除。 STL容器的析构函数将调用容器中所有元素的析构函数。

在容器中保持指针是个棘手的问题。由于指针没有析构函数,我想你永远不会想把原始指针放到STL容器中。以异常安全的方式执行此操作将非常困难,您必须使用try {} finally {}块来丢弃代码,以确保始终释放包含的指针。

那么你应该把什么放入容器而不是原始指针? +1 jmucchiello用于提升boost :: shared_ptr。 boost :: shared_ptr在STL容器中使用是安全的(与std :: auto_ptr不同)。它使用简单的引用计数机制,可以安全地用于不包含循环的数据结构。

对于包含周期的数据结构,您需要什么?在这种情况下,您可能希望升级到垃圾收集,这实际上意味着使用不同的语言,如Java。但这是另一个讨论。 ;)