【问题标题】:C++ delete, but not just the type [duplicate]C ++删除,但不仅仅是类型[重复]
【发布时间】:2013-07-05 08:26:37
【问题描述】:

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

char * memory = new char[256];

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

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

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

删除所有 256 字节的内存?

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

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

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

【问题讨论】:

  • 哦,是的。是否愿意阅读答案直到结束? “您不应该取消分配正在使用内存缓冲区的每个对象。相反,您应该 delete[] 只释放原始缓冲区。” - 此外,答案下的第三条评论:“严格来说,在原始字符缓冲区上调用 delete[] 是未定义的行为。”
  • 我不介意你是否愿意 - 这个答案回答了你的问题。
  • @DarioOO 不确定这是否只是示例,但是在您自己的缓冲区中创建对象时,请注意对齐。 IE。从您的示例 FooBar * obj = new (&memory[15]) FooBar();将减慢您的程序速度,因为您的对象中任何大于 sizeof char 的值都不会正确对齐。
  • 是的,placement new 有助于将对象与内存对齐,在这种情况下,为了简单起见,我将对象与 &memory[0] 对齐。顺便说一句,标准不允许我的代码(即使经过测试并在 MSVC 和 GCC 上工作)。
  • @Waldermort 啊,好吧 - 但我认为 DarioOO 在他写“标准不允许我的代码”时谈论的是删除的事情。

标签: c++ allocation placement-new delete-operator


【解决方案1】:

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

§5.3.5 删除

delete-expression 操作符销毁由 new-expression 创建的最派生对象 (1.8) 或数组。 删除表达式:

  • ::delete cast-expression
  • ::delete [ ] 强制转换表达式

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

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

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

  1. 指向对象的指针不是由 new 表达式创建的,而只是通过放置 new 完成对象构造。
  2. 地址obj 的指针(应该转换为void*memory 转换为void* 但afaik 你甚至不能依赖它)指向是使用@987654329 分配的@ 所以必须使用delete[]

查看相关问题:


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

emplace_back()push_back() 然后将就地构造对象。

【讨论】:

  • 即使测试在 2 个不同的编译器上运行,也未定义:/ 好的,谢谢
  • @DarioOO 是的。我认为使用存储分配字节的前置信息块的分配是相当普遍的,我认为释放内存的分配器对于deletedelete[] 是相同的,它们只是从ptr-size_of_info() 开始释放size_from_info()+size_of_info()。有关说明,请参阅 this answer。但即使它很常见,也不意味着您可以在任何“现实世界代码”中依赖它。很高兴知道一些好玩的东西......
  • 非常有趣的链接!谢谢:)
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-24
    • 2021-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-15
    相关资源
    最近更新 更多