(注意:这个问题的动机是试图提出预处理器hackery来生成无操作分配来回答这个问题:
...请记住这一点!)
这是一个人为的课程:
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激发它吗?
答案 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
来覆盖第一个对象而不首先明确它称它为析构函数,这至少会造成内存泄漏。