13

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

接受新对象的宏

......所以请记住这一点!)

这是一个人为的类:

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 来驱动它?

4

3 回答 3

16

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

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

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

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

于 2011-09-23T04:52:46.263 回答
3
foo* p1 = new (buffer) foo(1);
foo* p2 = new (buffer) foo(2);
p1->~foo();
p2->~foo();

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

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

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

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

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

于 2011-09-23T04:36:33.177 回答
0

不——这看起来不对。

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

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

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

于 2011-09-23T04:41:47.333 回答