7

stl中的vector之类的容器对象如何被销毁,即使它们是在堆中创建的?

编辑

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

4

12 回答 12

20

指针的 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 不会泄露任何数据。

于 2008-12-20T08:08:36.950 回答
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.

(如果你想要一个在销毁时删除指针的向量类型,当然可以编写这样的类型。可能已经编写了。但要小心删除其他人仍然持有副本的指针。)

于 2008-12-20T08:09:01.050 回答
3

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

于 2008-12-20T06:55:09.343 回答
3

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

  1. 向量实例
  2. 实际的底层数组实现

虽然是可配置的,但#2 几乎总是存在于堆上。#1 但是可以存在于堆栈或堆上,它只取决于它的分配方式。例如

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

在这种情况下,第 1 部分位于堆栈中。

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

于 2008-12-20T07:04:38.670 回答
2

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

这样做的一个好的副作用是,您可以在程序中保留多个指针容器,但这些对象只属于其中一个容器,您只需要清理那个容器。

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

for (ContainerType::iterator it(container.begin()); it != container.end(); ++it)
{
    delete (*it);
}
于 2008-12-20T09:22:51.870 回答
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

于 2008-12-20T13:04:33.000 回答
0

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

于 2008-12-20T06:32:49.933 回答
0

要回答您的第一个问题:

STL 类没有什么特别之处(我希望如此)。它们的功能与其他模板类完全相同。因此,如果在堆上分配它们,它们不会自动销毁,因为 C++ 对它们没有垃圾收集(除非你用一些花哨的 autoptr 业务或其他东西告诉它)。如果您在堆栈上分配它(没有 new),它很可能会由 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[] 时,堆上分配了十个对象的空间,并构造了十个对象。类似地,当在析构函数中调用 delete[] 时,十个对象被解构,然后之前分配的内存被释放。

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

于 2008-12-20T07:57:39.607 回答
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>() );

但是当向量的内容要……嗯……共享时,你应该使用共享指针。是的。

于 2008-12-20T13:42:32.330 回答
0

从 STL 序列容器中删除指针的函子

于 2008-12-20T18:05:25.857 回答
0

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

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

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

我更喜欢指针容器:
指针容器与 STL 容器相同,只是您将指针放入其中,但容器随后会获得指针指向的对象的所有权,因此会在容器被销毁。

当您访问 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<> 在这种情况下不是智能指针的有效选择。

这是因为 STL 容器假定它们包含的对象是可复制的,不幸的是 std::auto_ptr<> 在传统意义上是不可复制的,因为它破坏了复制的原始值,因此复制的源不能是 const。

于 2008-12-20T22:58:48.867 回答
0

STL 容器与任何其他对象一样,如果您实例化一个容器,它将在堆栈上创建:

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

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

将指针保存在容器中是一个冒险的问题。由于指针没有析构函数,我会说你永远不想将原始指针放入 STL 容器中。以异常安全的方式执行此操作将非常困难,您必须在代码中添加 try{}finally{} 块,以确保始终释放包含的指针。

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

包含循环的数据结构需要什么?在这种情况下,您可能想学习垃圾收集,这实际上意味着使用不同的语言,例如 Java。但这是另一个讨论。;)

于 2009-08-05T05:08:36.127 回答