std :: vector销毁和意外的内存泄漏

时间:2011-07-09 23:15:08

标签: c++ memory-leaks vector

考虑以下示例:

#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.根据{{​​3}},现在应该调用向量v的每个元素的析构函数。

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

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

我在这里缺少什么?

4 个答案:

答案 0 :(得分:10)

std::vector<T>确实在T被销毁时调用它的析构函数。此处Tint *int *的析构函数什么也没做。 int *本身的存储空间被释放,但它们指向的int不是。

考虑:

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

这表现出同样的问题;当x超出范围时,它的析构函数确实被调用,而指针x 的存储被释放,但由于指针的析构函数是否为-op,指向 int未被释放。

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

您需要使用int,或使用智能指针,例如std::vector<int>。请注意,使用智能指针可能会增加开销;使用C ++ 0x,您应该能够将std::vector<boost::shared_ptr<int> >std::vector<std::unique_ptr<int>>结合使用以避免此开销。 Boost还有pointer vectors,可以释放指向你想要的元素。

答案 1 :(得分:4)

  

现在应该调用向量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;它与标准库容器,因为它不是真正可复制的)。如果你不使用智能指针,那么你需要自己销毁动态对象;正确地做这件事是相当困难的。

答案 2 :(得分:2)

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

答案 3 :(得分:2)

由于您使用的是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;
}