std::auto_ptr、delete[]和内存泄漏

7

为什么这段代码不会导致内存泄漏?

int iterCount = 1000;
int sizeBig = 100000;
for (int i = 0; i < iterCount; i++)
{
   std::auto_ptr<char> buffer(new char[sizeBig]);
}

操作系统:WinXP sp2,编译器:BCB.05.03


在大多数情况下,如果您将new与delete[]配对,反之亦然,对于非平凡类型,这并不会导致泄漏 - 相反,程序在尝试释放内存时会崩溃。 - sharptooth
3
您可以尝试使用 boost::scoped_array 替代 std::auto_ptr。 - Harald Scheirich
2个回答

15

这是因为你(不)幸运。 auto_ptr 调用的是 delete,而非 delete []。这是未定义的行为。

尝试像下面这样做并看看你是否能如此幸运:

struct Foo
{
    char *bar;
    Foo(void) : bar(new char[100]) { }
    ~Foo(void) { delete [] bar; }
}

int iterCount = 1000;
int sizeBig = 100000;
for (int i = 0; i < iterCount; i++)
{
   std::auto_ptr<Foo> buffer(new Foo[sizeBig]);
}

这里的想法是,Foo 的析构函数将不会被调用。


原因是这样的:当你使用 delete[] p 时,delete[] 的实现应该会遍历数组中的每个元素,调用它们的析构函数,然后释放指向 p 的内存。同样地,delete p 应该会先调用 p 的析构函数,然后释放内存。

char 类型没有析构函数,所以它仅会删除指向 p 的内存。在我上面的代码中,它不会析构数组中的每个元素(因为它没有调用 delete[]),因此一些 Foo 可能会保留其本地 bar 变量而未被删除。


是的,我很不幸 :) 感谢您的解释。 - Mikhail Aksenov
我编译了这个程序,但是我收到了“_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)”错误消息,程序崩溃了。这似乎表明它被删除两次了,但我不知道怎么会发生这种情况。 - zar
Foo的析构函数实际上会被调用iterCount次,但请注意是iterCount * sizeBig次。 - zar

3

auto_ptr只在循环迭代期间存活,并在迭代完成时释放连接到它的对象。

编译器可以看到,在这种情况下,new[] 可以像 new一样分配空间,而无需存储元素的数量,因为不需要调用微不足道的char 析构函数。这就是为什么当 auto_ptr 的析构函数调用 delete 而非 delete[] 时,它不会引起问题,因为内存块实际上是按照 new 的方式分配的,而该分配可以与 delete 配对。

这是一个不应该做的事情的例子。编译器决定是否将 new[] 替换为 new。使用 delete 而不是 delete[] 或反之则是未定义的行为。

请参见为什么会有意地这样写?(有意不使用delete []来处理数组) 进行delete vs delete[]的讨论。


正如GMan所说:这里的delete是否等同于*delete[]*,纯属运气? - xtofl
1
嗯,是的,这是编译器试图聪明地操作并意外地拯救开发者的结果。 - sharptooth
3
这并不是编译器聪明的结果,而是编译器懒惰的结果,它只做最简单的事情(在结构体仅包含 PODs 时,对一个元素和数组进行内存分配实际上是完全相同的)。当然,这是未定义的行为,所以它“幸运地”在某些实现中可以工作 - 我记得有一些真正的实现它是不起作用的。 - Pavel Minaev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接