我有一个班级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);
有人可以告诉我为什么以及内存分配/释放发生了什么?
答案 0 :(得分:3)
当然,您无法在实例上调用delete
,假设它会执行new
的计数器并释放内存。它无法知道您使用了展示位置 - new
来为对象分配后备存储。我认为最终的片段也是正确的:运行析构函数,并手动释放内存,因为只有你知道它来自哪里。
答案 1 :(得分:2)
分配器通常在节点旁边存储分配信息(链接列表指向下一个节点,大小等......)。
因此,您只能为分配的同一对象调用deallocator(delete
)。否则你处于UB区域,解除分配器会将内存的错误部分解释为存储额外信息,并且您的代码可能会崩溃..
最后一个例子是正确的。
但是,我认为你可能会遇到对齐问题,因为内存是为 char[]
而不是A
分配的。
答案 2 :(得分:2)
new A
最常见的实现是分配内存足以存储1 A
个对象并返回分配地址(以及之后调用该对象的构造函数)。
new A[x]
必须跟踪分配的x
个A
个对象。 x
通常存储在第一个分配字段中,new[]
会在该字段后面返回地址(因此它指向数组的实际开始)。
在你搞砸的情况下,根本没有分配第一个领域。 delete[]
无法确定A
的数量(它会尝试,但在给定指针的负偏移处会有堆块标头)。因此,您必须为每个对象显式调用析构函数,然后使用delete
或delete[]
为任何内置类型指针释放内存,否则它将尝试调用析构函数。
修改 :(忘记添加)
在通常的实现中,堆分配的内置类型数组没有数量字段,因为它们不需要调用析构函数。这就是为什么new char[]
delete [] reinterpret_cast<char *>(q)
与new[]
和delete[]
的{{1}}不同。
答案 3 :(得分:1)
是的,最后一个例子是正确的。
经验法则是你只能delete
new
得到的东西(当然只有一次)。数组版本也是如此。
您从new[]
得到的是char*
,我认为您删除它的方式很清楚。
此外,如果使用placement-new构造对象,则需要手动调用析构函数(如果需要)。