Placement new on non-pointer variables and class members

久未见 提交于 2020-01-14 16:46:31

问题


Consider the following example:

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

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.

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.

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

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.

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

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.

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
}

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


回答1:


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

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

  • 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

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

emphasis mine

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



来源:https://stackoverflow.com/questions/58274963/placement-new-on-non-pointer-variables-and-class-members

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!