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

发布于 2024-12-06 05:49:51 字数 1072 浏览 1 评论 0原文

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

接受新对象的宏

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

这是一个人为的类:

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 引发此问题的更简单的应用程序中?

(Note: this question was motivated by trying to come up with preprocessor hackery to generate a no-op allocation to answer this other question:

Macro that accept new object

...so bear that in mind!)

Here's a contrived class:

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; }
};

...which I will allocate like this:

// 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();

On the gcc I've got around, I get the "expected" result:

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

Which is great, but could the compiler/runtime reject this as an abuse and still be on the right side of the spec?

How about with threading? If we don't actually care about the contents of this class (let's say it's just a dummy object anyway) will it at least not crash, such as in the even simpler application which motivated this with a POD int?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不乱于心 2024-12-13 05:49:51

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

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

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

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

Peforming placement-new several times on the same block of memory is perfectly fine. Moreover, however strange it might sound, you are not even requred to destruct the object that already resides in that memory (if any). The standard explicitly allows that in 3.8/4

4 A program may end the lifetime of any object by reusing the storage
which the object occupies or by explicitly calling the destructor for
an object of a class type with a non-trivial destructor. For an object
of a class type with a non-trivial destructor, the program is not
required to call the destructor explicitly before the storage which
the object occupies is reused or released;[...]

In other words, it is your responsibility to take into account the consequences of not calling the destructor for some object.

However, calling the destructor on the same object twice as you do in your code is not allowed. Once you created the second object in the same region of memory, you effectively ended the lifetime of the first object (even though you never called its destructor). Now you only need to destruct the second object.

生死何惧 2024-12-13 05:49:51
foo* p1 = new (buffer) foo(1);
foo* p2 = new (buffer) foo(2);
p1->~foo();
p2->~foo();

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

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

更新:
关于标题中提到的问题,

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

是的,只要它指向原始内存,它就可以在同一地址多次放置 new 吗?

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

You are destructing the same object twice, and that alone is undefined behavior. Your implementation may decide to order a pizza when you do that, and it would still be on the right side of the spec.

Then there is the fact that your buffer may not be properly aligned to emplace an object of type foo, which is again non standard C++ (according to C++03, I think C++11 relaxes this).

Update:
Regarding the question specified in the title,

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

Yes, is it well-defined to placement-new multiple times at the same address, provided that it points to raw memory.

夏有森光若流苏 2024-12-13 05:49:51

不——这看起来不对。

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

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

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

No - this doesn't look right.

When you use placement new, the object will be constructed at the address you pass. In this example you're passing the same address (i.e. &buffer[0]) twice, so the second object is just obliterating the first object that's already been constructed at this location.

EDIT: I don't think I understand what you're trying to do.

If you have a general object type (that may have non-trivial ctor/dtor's that might allocate/deallocate resources) and you obliterate the first object by placement new'ing over the top of it without first explicitly calling it's destructor, this will at least be a memory leak.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文