在同一地址多次放置新的定义/合法吗?

时间:2011-09-23 04:30:42

标签: c++ undefined-behavior language-lawyer placement-new

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

Macro that accept new object

...请记住这一点!)

这是一个人为的课程:

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 个答案:

答案 0 :(得分:15)

在同一块内存中多次执行 placement-new 非常好。而且,无论听起来多么奇怪,你甚至都不需要破坏已经存在于该内存中的对象(如果有的话)。该标准明确允许在3.8 / 4

  

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

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

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

答案 1 :(得分:3)

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

您正在两次破坏同一个对象,而这仅仅是未定义的行为。当您这样做时,您的实施可能会决定订购比萨饼,并且它仍然位于规范的右侧。

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

<强>更新 关于标题中指定的问题,

  

在同一地址多次放置新的定义合法/合法吗?

是的,只要它指向原始内存,它是否在同一地址多次放置新定义。

答案 2 :(得分:0)

不 - 这看起来不对。

当您使用展示位置new时,该对象将在您传递的地址 处构建。在这个例子中,你传递相同的地址(即&amp; buffer [0])两次,所以第二个对象只是删除了已经在这个位置构建的第一个对象。

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

如果你有一般的对象类型(可能有可能分配/解除分配资源的非平凡的ctor / dtor),你可以通过放置new来覆盖第一个对象而不首先明确它称它为析构函数,这至少会造成内存泄漏。