如何分配内存以及如何解除分配?

时间:2014-01-24 12:56:15

标签: c++ memory-management memory-leaks

我有一个班级A,我创建了一个指针:

A * const q = reinterpret_cast<A * const>(new char[2 * sizeof(A)]);

我有2个objets:

new (q)A;
new (q + 1) A;

如果我删除指针,如:

  • delete q;它一次调用A
  • 的析构函数
  • delete[] reinterpret_cast<char *>(q);没有~A()
  • 的电话
  • delete q; delete (q+1);没有订单崩溃
  • delete [] q也崩溃了

正确的方法,我认为是这样的:

q->~A();
q[1].~A();
delete[] reinterpret_cast<char *>(q);

有人可以告诉我为什么以及内存分配/释放发生了什么?

4 个答案:

答案 0 :(得分:3)

当然,您无法在实例上调用delete,假设它会执行new的计数器并释放内存。它无法知道您使用了展示位置 - new来为对象分配后备存储。我认为最终的片段也是正确的:运行析构函数,并手动释放内存,因为只有你知道它来自哪里。

答案 1 :(得分:2)

分配器通常在节点旁边存储分配信息(链接列表指向下一个节点,大小等......)。

因此,您只能为分配的同一对象调用deallocator(delete)。否则你处于UB区域,解除分配器会将内存的错误部分解释为存储额外信息,并且您的代码可能会崩溃..

最后一个例子是正确的。

但是,我认为你可能会遇到对齐问题,因为内存是为char[]而不是A分配的。

答案 2 :(得分:2)

new A最常见的实现是分配内存足以存储1 A个对象并返回分配地址(以及之后调用该对象的构造函数)。

另一方面,

new A[x]必须跟踪分配的xA个对象。 x通常存储在第一个分配字段中,new[]会在该字段后面返回地址(因此它指向数组的实际开始)。

在你搞砸的情况下,根本没有分配第一个领域。 delete[]无法确定A的数量(它会尝试,但在给定指针的负偏移处会有堆块标头)。因此,您必须为每个对象显式调用析构函数,然后使用deletedelete[]为任何内置类型指针释放内存,否则它将尝试调用析构函数。

修改 :(忘记添加)

在通常的实现中,堆分配的内置类型数组没有数量字段,因为它们不需要调用析构函数。这就是为什么new char[] delete [] reinterpret_cast<char *>(q)new[]delete[]的{​​{1}}不同。

答案 3 :(得分:1)

是的,最后一个例子是正确的。

经验法则是你只能delete new得到的东西(当然只有一次)。数组版本也是如此。

您从new[]得到的是char*,我认为您删除它的方式很清楚。

此外,如果使用placement-new构造对象,则需要手动调用析构函数(如果需要)。