在同一地址多次放置新的是否定义明确/合法

Is it well-defined/legal to placement-new multiple times at the same address?

本文关键字:定义 是否 合法 地址      更新时间:2023-10-16

(注意:这个问题的动机是试图想出预处理器技巧来生成一个无操作分配来回答另一个问题:

接受新对象的宏

所以请记住!(

这是一个人为设计的类:

class foo {
private:
    int bar;
public:
    foo(int bar) : bar (bar)
        { std::cout << "construct foo #" << bar << std::endl; }
    ~foo()
        { std::cout << "destruct foo #" << bar << std::endl; }
};

我会这样分配:

// Note: for alignment, don't use char* buffer with new char[sizeof(foo)] !
void* buffer = operator new(sizeof(foo));
foo* p1 = new (buffer) foo(1);
foo* p2 = new (buffer) foo(2);
/* p1->~foo(); */ /* not necessary per spec and problematic in gen. case */
p2->~foo();

在我周围的gcc上,我得到了"预期"的结果:

construct foo #1
construct foo #2
destruct foo #2

这很好,但编译器/运行时是否会将其视为滥用而拒绝,并且仍然站在规范的正确一边?

穿线怎么样?如果我们实际上不关心这个类的内容(假设它只是一个伪对象(,它至少不会崩溃吗,比如在更简单的应用程序中,它用POD int来驱动这个?

在同一内存块上多次执行放置新是非常好的。此外,无论听起来多么奇怪,您甚至不需要销毁已经驻留在该内存中的对象(如果有的话(。该标准明确允许在3.8/4 中

4程序可以通过重复使用存储来结束任何对象的生命周期对象占用的或通过显式调用其析构函数具有非平凡析构函数的类类型的对象。对于对象对于具有非平凡析构函数的类类型,程序不是需要在存储之前显式调用析构函数占用的对象被重复使用或释放;[…]

换句话说,您有责任考虑不为某个对象调用析构函数的后果。

但是,不允许像在代码中那样对同一对象调用析构函数两次。一旦在同一内存区域中创建了第二个对象,就有效地结束了第一个对象的生存期(尽管从未调用过它的析构函数(。现在你只需要破坏第二个对象。

foo* p1 = new (buffer) foo(1);
foo* p2 = new (buffer) foo(2);
p1->~foo();
p2->~foo();

您正在破坏同一个对象两次,仅此一次就是未定义的行为。当你这样做的时候,你的实现可能会决定点披萨,而且它仍然在规范的右侧。

还有一个事实是,您的缓冲区可能没有正确对齐以放置foo类型的对象,这也是非标准C++(根据C++03,我认为C++11放宽了这一点(。

更新:关于标题中指定的问题,

在同一地址多次放置新的是否定义明确/合法?

是的,如果它指向原始内存,那么在同一地址多次放置新内存是否定义良好。

否-这看起来不对。

当您使用放置new时,对象将在您传递的地址处构造。在本例中,您将两次传递同一地址(即&buffer[0](,因此第二个对象只是擦除已在该位置构造的第一个对象。

编辑:我想我不明白你想做什么。

如果您有一个通用的对象类型(可能有可能分配/释放资源的非平凡的ctor/dtor(,并且您通过将new放在第一个对象的顶部而不首先显式调用它的析构函数来擦除第一个对象,那么这至少是内存泄漏。