【问题标题】:Why does calling delete instead of delete[] on an array of class objects cause heap corruption?为什么在类对象数组上调用 delete 而不是 delete[] 会导致堆损坏?
【发布时间】:2013-02-11 00:14:15
【问题描述】:

考虑代码:

class A {
public:
    virtual ~A() {}
};

class B : public A {
public:
    ~B() {}
};

void main ()
{
    A * array = new A[100];
    delete array;
}

在 Windows (MSVC 2010) 上,它会导致异常,因为 delete 调用 HeapValidate,这表明堆已损坏。这是如何以及为什么会发生的?

我确实意识到应该在这里调用delete[],当然这样就没有问题了。但是为什么delete 会导致堆损坏?据我所知,它应该为第一个对象(array[0]*array)调用析构函数,然后释放整个块。现实中会发生什么?

注意:如果类A只有默认析构函数,即我根本没有声明它的析构函数,则不会发生异常。不管析构函数是否是虚拟的。在调试和发布版本中。

P。 S. 是的,我知道这是未定义的行为。

【问题讨论】:

  • 可能是因为new[] 的作用与new 不同。

标签: c++ windows memory-management destructor


【解决方案1】:

在使用new[] 创建的指针上调用delete 是未定义的行为。基本问题是,当您调用new[] 时,它需要分配额外的空间来存储数组中元素的数量,这样当您调用delete [] 时,它就知道要销毁多少元素。

除了实际对象所需的空间外,库还将为管理数据分配空间。然后它将执行所有初始化并返回一个指向第一个元素的指针,该元素与从操作系统检索的内存块不对齐。

[header][element1,element2...]
^       ^
|       \_ pointer returned by new[]
|
\_ pointer returned by the allocator

另一方面,newdelete 不存储任何额外信息。

当您调用delete[] 时,它会将指针移回,读取计数,调用析构函数并使用原始指针解除分配。当您调用 delete 时,它​​会调用单个对象的析构函数并将指针传递回分配器。如果指针是通过调用new[] 创建的,则返回给分配器的指针分配的指针不同,并且释放失败。

【讨论】:

  • 请注意,第一句话是关于此事的所有标准。以下是典型实现可能会做的事情;该标准没有强制要求,细节可能因平台或工具链而异。 +1
  • 谢谢!但它并没有解释析构函数在这里扮演什么角色。如果它像您描述的那样简单,那么无论如何它都会失败。但它只会在声明析构函数时失败,即使是空体。
  • 部分编译器optimize that case.
  • @RaymondChen:这就解释了!谢谢。您应该将此作为答案。
【解决方案2】:

因为它是未定义的行为。任何事情都有可能发生。

【讨论】:

  • 这在某种程度上回答了“为什么”,而不是“如何”。
  • @Violet “如何”隐藏在编译器内部,深入庞大的代码库中,并且对于每个编译器和编译器版本都会有所不同。对于特定的未定义行为案例,除了 Microsoft 之外,任何人都不太可能告诉您幕后发生的事情。
  • @DavidRodríguez-dribeas:确实!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-18
  • 1970-01-01
  • 1970-01-01
  • 2010-12-27
  • 2021-06-16
  • 1970-01-01
  • 2011-02-14
相关资源
最近更新 更多