非指针变量和类成员上的新放置 [英] Placement new on non-pointer variables and class members

查看:121
本文介绍了非指针变量和类成员上的新放置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下示例:

#include <iostream>

struct A {

    int i;

    A(int i)
    {
        this->i = i;
    }

    A &operator=(const A &a) = delete;
    A(const A &a) = delete;
};

int main()
{
    A a(1);
    new(&a) A(5);
    //a = A(7); // not allowed since = is deleted in A
    std::cout << a.i << std::endl;
}

这是一个使用placement new运算符的简单示例.由于struct A的副本构造函数和赋值运算符已被删除(无论出于何种原因),因此无法更改变量A a所保存的对象,除非将其地址传递给placement new运算符.

This is a simple example using the placement new operator. Since the copy constructor and assignment operator of struct A have been deleted (for whatever reason), it is not possible to change the object the variable A a holds, except for passing its address to the placement new operator.

这样做的原因可能是struct A拥有大型数组(例如100M条目),这些数组必须在赋值运算符和复制构造函数中进行复制.

Reasons for this might include that struct A holds large arrays (e.g. 100M entries) which would have to be copied in the assignment operator and the copy constructor.

问题的第一部分围绕这种方法的合法性".我发现了这个 stackoverflow问题,其可接受的答案是

The first part of the question revolves around the "legality" of this approach. I found this stackoverflow question, the accepted answer of which says

这是完全合法的. 而且没有用,因为在放置新的位置之后,无法使用var [A a在这种情况下]引用存储在其中的[object]的状态.任何此类访问都是未定义的行为. […]在任何情况下都不能在将new放置在var上后再引用它.

this is perfectly legal. And useless, because you cannot use var [A a in this case] to refer to the state of the [object] you stored within it after the placement new. Any such access is undefined behavior. […] under no circumstance may you ever refer to var after you placement new'd over it.

为什么会这样呢?我还看到了其他一些关于放置new运算符的示例,这些示例始终类似于

Why would that be the case? I have seen several other examples for the placement new operator, which are always similar to

A a(1);
A *b = new(&a) A(2);
// Now use *b instead of a

根据我的理解,使用A a还是A *b来访问对象都没有关系,因为新放置会替换A a地址(当然 <)上的对象A a.也就是说,我希望总是 b == &a.也许答案还不够清楚,并且此限制是由于类成员的稳定性所致.

From my understanding it should not matter whether A a or A *b is used to access the object since the placement new replaces the object at the address of A a which of course is A a. That is, I would expect that always b == &a. Maybe the answer was not clear enough and this limitation is due to the const-ness of the class member.

这里是具有相同想法的另一个示例,但是这次struct A被嵌入到另一个对象中:

Here is another example with the same idea, however this time struct A is embedded into another object:

#include <iostream>

struct A {

    int *p;

    A(int i)
    {
        p = new int(i);
    }

    ~A()
    {
        delete p;
    }

    A &operator=(const A &a) = delete;
    A(const A &a) = delete;
};

struct B {

    A a;

    B(int i) : a(i)
    {
    }

    void set(int i)
    {
        a.~A(); // Destroy the old object
        new(&a) A(i);
    } 

};

int main()
{
    B b(1);
    b.set(2);
    std::cout << *(b.a.i) << std::endl;
    // This should print 2 and there should be no memory leaks
}

问题与相同的推理基本相同.重新放置到地址&a是否有效?

The question is basically the same with the same reasoning. Is it valid to placement-new into the address &a?

推荐答案

对于此特定代码,您还可以,并且可以使用a引用放置在其上的新对象. [basic.life]/8

For this specific code, you are okay and can use a to refer to the new object that you put in its place. This is covered by [basic.life]/8

如果在一个对象的生命周期结束之后并且在重新使用或释放​​该对象所占用的存储之前,在原始对象所占用的存储位置上创建了一个指向原始对象的指针的新对象,引用原始对象的引用,或者原始对象的名称将自动引用新对象,并且一旦新对象的生命周期开始,就可以用来操纵新对象,如果:

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:

  • 新对象的存储空间正好覆盖了原始对象所占据的存储位置,并且

  • the storage for the new object exactly overlays the storage location which the original object occupied, and

新对象与原始对象具有相同的类型(忽略顶级cv限定词),并且

the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and

原始对象的类型不是const限定的,并且,如果是类类型,则不包含任何类型为const限定的非静态数据成员或引用类型,并且

the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and

原始对象和新对象都不是潜在重叠的子对象([intro.object]).

neither the original object nor the new object is a potentially-overlapping subobject ([intro.object]).

强调我的

您选中了所有这些要求,因此a将引用新的"您放置在a内存中的A.

You check off all of those requirements so a will refer to the "new" A that you placed in a's memory.

这篇关于非指针变量和类成员上的新放置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆