考虑以下示例:
#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
我在这里缺少什么?
答案 0 :(得分:10)
std::vector<T>
确实在T
被销毁时调用它的析构函数。此处T
为int *
。 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_ptr
或shared_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;
}