【问题标题】:std::vector Destruction and Unexpected Memory Leakstd::vector 破坏和意外的内存泄漏
【发布时间】:2011-10-02 01:25:38
【问题描述】:

考虑以下示例:

#include <vector>
class Foo {
    std::vector<int*> v;
public:
    Foo() {
        this->v.push_back(new int(23));
        this->v.push_back(new int(24));
        this->v.push_back(new int(25));
    }

    ~Foo() {
    }
};

int main() {
    Foo f;
    return 0;
}

当 f 超出 main() 的范围时,会调用 f 的析构函数,这应该间接释放 f.v。根据this,现在应该调用向量 v 的每个元素的析构函数。

但是,当我在 valgrind 中运行这个程序时,我发现 int* 没有被释放。

$ valgrind --leak-check=full ./a.out

我在这里错过了什么?

【问题讨论】:

标签: c++ memory-leaks vector


【解决方案1】:

现在应该调用向量v 的每个元素的析构函数

是的:存储在向量中的int* 对象被销毁(这实际上是一个无操作)。容器中的指针指向的对象不会被销毁。

考虑以下同样有效的程序:

{
    int x;
    std::vector<int*> v;
    v.push_back(&x);
}   // x cannot be delete'd because it isn't dynamically allocated.

您应该使用智能指针,例如std::unique_ptrshared_ptr,这样您就不必担心内存管理(不要使用std::auto_ptr;它与标准库容器不兼容,因为它不是'不是真正可复制的)。如果您不使用智能指针,那么您需要自己销毁动态对象;正确地做到这一点相当困难。

【讨论】:

  • 请注意,unique_ptr 只能在使用 C++0x 的 STL 容器中使用,并且在插入项目时使用 std::move
【解决方案2】:

向量的每个元素都是一个int *。当 int * 被销毁时,语言不会自动调用 delete 。换句话说,被销毁的是指针,而不是指针。

【讨论】:

    【解决方案3】:

    std::vector&lt;T&gt; 在销毁时确实调用了T 的析构函数。这里Tint *int * 的析构函数什么都不做。 int * 本身的存储空间被释放,但他们指向的 ints 没有。

    考虑:

    int main() {
       int *x = new int(23);
       return 0;
    }
    

    这表现出同样的问题;当x 超出范围时,它的析构函数确实被调用,并且x 指针的存储空间被释放,但是由于指针的析构函数是空操作, 指向 int 未被释放。

    更重要的是,vector 不知道ints 是如何分配的。它们可能由new int 分配,但它们也可以指向分配有new int[200] 的数组中的元素,或者它们可能指向malloc'd 数据,或者它们可能指向mmap'd 缓冲区,或者它们可能指向结构元素,或者两个向量可能指向相同的 ints... 等等。vector 不够聪明,无法判断你想用这些做什么,所以它让它们一个人呆着(此外,给vector 逻辑来删除指向的元素会破坏非指针元素的向量,例如std::vector&lt;int&gt;,因为你不能deleteint!)

    您需要使用std::vector&lt;int&gt;,或者结合使用智能指针,例如std::vector&lt;boost::shared_ptr&lt;int&gt; &gt;。请注意,使用智能指针可能会增加开销;使用 C++0x,您应该能够将 std::vector&lt;std::unique_ptr&lt;int&gt;&gt;std::move 结合使用,以避免这种开销。 Boost 也有 pointer vectors,它也可以像您预期的那样释放指向的元素。

    【讨论】:

    • 是的,这回答了我的问题。尽管指针本身已释放,但 int* 指向的内存并未释放。谢谢!
    【解决方案4】:

    由于您使用new 关键字,整数被分配在堆而不是堆栈上。换句话说,它们是动态分配的。换句话说,你需要在它之后进行清理。

    指针类型的“析构函数”是简单地删除该指针。它不会触及位于指针存储的内存地址的数据。考虑以下示例:

    int a = 5;
    int* i = &a;
    if (true)
    {
       int* j = i;
    } //j goes out of scope, should *i and a be deleted? no.
    

    所以你需要在析构函数中这样做:

    std::vector<int*>::iterator iter;
    for (iter = v.begin(); iter != v.end(); iter++)
    {
       delete *iter;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-02-19
      • 1970-01-01
      • 2016-02-07
      • 1970-01-01
      • 2019-03-01
      • 2016-05-01
      • 2011-01-10
      相关资源
      最近更新 更多