3

假设我只想分配 256 字节的内存块

char * memory = new char[256];

比我使用placement new 创建一个FooBar 对象(sizeof(Foobar)<=256)

FooBar * obj = new (memory) FooBar();

delete obj; //this also calls the destructor of FooBar

删除所有 256 字节的内存?

标准是否保证仅通过“删除 obj”来释放整个“内存”缓冲区?或者它基于“FooBar”类型,因此该操作具有未定义的行为?

假设: FooBar 是内存缓冲区中的唯一对象。

这不是重复的问题,请先了解问题。这段代码的作用还不是很明显。

4

2 回答 2

6

C++ 标准告诉你为什么你在做什么是未定义的。可能是这样的情况,调用的释放delete使用已分配块的前置大小信息并正确释放它,但您不能依赖它。

§5.3.5 删除

delete-expression 运算符破坏由 new-expression 创建的最派生对象 (1.8) 或数组。删除表达式:

  • ::删除强制转换表达式
  • ::delete [ ] 强制转换表达式

第一种选择用于非数组对象,第二种选择用于数组。每当 delete 关键字紧跟空方括号时,它应被解释为第二种选择。[...]

话虽如此,现在我们来到更有趣的部分:

2 [...] 在第一种选择(删除对象)中,delete 的操作数的值可以是空指针值、指向由先前的 new 表达式创建的非数组对象的指针,或者指向子对象(1.8),表示这种对象的基类(第 10 条)。如果不是,则行为未定义。[...]

  1. 指向对象的指针不是由 new 表达式创建的,而是仅通过放置 new 完成对象构造。
  2. 指向地址的指针obj(应该与cast tovoid*相同,但你甚至不能依赖它)指向的指针是使用分配的,因此使用是强制性的。memoryvoid*new[]delete[]

请参阅这些相关问题:


如果您想为多个相同类型的对象预分配存储空间:请考虑使用std::vector<FooBar>::reserve().

emplace_back()或者push_back()然后将对对象进行就地构造。

于 2013-07-08T05:35:01.720 回答
5

这确实是未定义的行为,正确的做法是显式调用在该内存块中分配的每个对象的析构函数,然后释放内存块本身。

obj->~FooBar();
delete[] memory;

请记住,placement new 只是构造对象,它不分配内存,所以对象不应该释放它。如果你分配内存,你应该释放它,如果你构造了对象,你应该只解构它。

于 2013-07-08T05:11:37.027 回答