另一个对象重用对象的 space

reusing object's space by another object

我希望通过该指针在基 class 中重新使用分配的 space,但 C++ 标准不赞成。但是,标准的措辞似乎是错误的。它提出了一个条件"and before the storage which the object occupied is reused or released",但它显然在他们自己的代码片段中被重用了。我哪里弄错了?

void B::mutate() {
  new (this) D2;    // reuses storage — ends the lifetime of *this!! REUSED AS WELL SO CONDITION SO RESTRICTIONS DON'T HOLD ANYMORE!
  f();              // undefined behavior

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated41 or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways. For an object under construction or destruction, see [class.cdtor]. Otherwise, such a pointer refers to allocated storage ([basic.stc.dynamic.deallocation]), and using the pointer as if the pointer were of type void*, is well-defined. Indirection through such a pointer is permitted but the resulting lvalue may only be used in limited ways, as described below. The program has undefined behavior if:

(6.1) the object will be or was of a class type with a non-trivial destructor and the pointer is used as the operand of a delete-expression,

(6.2) the pointer is used to access a non-static data member or call a non-static member function of the object, or

(6.3) the pointer is implicitly converted ([conv.ptr]) to a pointer to a virtual base class, or

(6.4) the pointer is used as the operand of a static_­cast, except when the conversion is to pointer to cv void, or to pointer to cv void and subsequently to pointer to cv char, cv unsigned char, or cv std​::​byte ([cstddef.syn]), or

(6.5) the pointer is used as the operand of a dynamic_­cast.

[ Example:

    #include <cstdlib>
    struct B {
      virtual void f();
      void mutate();
      virtual ~B();
    };

    struct D1 : B { void f(); };
    struct D2 : B { void f(); };

   /* RELEVANT PART STARTS */ void B::mutate() {
      new (this) D2;    // reuses storage — ends the lifetime of *this
      f();              // undefined behavior 
      /* RELEVANT PART ENDS */
      ... = this;       // OK, this points to valid memory
    }

   void g() {
      void* p = std::malloc(sizeof(D1) + sizeof(D2));
      B* pb = new (p) D1;
      pb->mutate();
      *pb;              // OK: pb points to valid memory
      void* q = pb;     // OK: pb points to valid memory
      pb->f();          // undefined behavior, lifetime of *pb has ended
    }

but it is clearly reused in their own code snippet.

new (this) D2;    // reuses storage — ends the lifetime of *this
f();              // undefined behavior**
... = this;       // OK, this points to valid memory

正确。因为存储已经被重复使用,所以“Otherwise”条款适用:

... Otherwise, such a pointer refers to allocated storage ([basic.stc.dynamic.deallocation]), and using the pointer as if the pointer were of type void*, is well-defined.

不可能通过 void* 调用 f(),因此该子句不允许这样做。否则,调用生命周期结束的对象的成员函数是未定义的(在析构函数之外)。

... = this; 另一方面 可以用 void*.

完成的事情

请注意 (new (this) D2)->f() 将是 well-defined。

当你这样做时

f();

在成员函数中你真正做的是

this->f();

所以在他们这样做的例子中

new (this) D2; 

它结束了 this 指向的事物指针的生命周期,并在它的位置创建了一个新的 D2。这让你认为 this->f(); 没问题,因为 this 现在指向一个已经开始其生命周期的对象,但你忘记了 this 是一个指向已经有生命周期的对象的指针结束了。您不能使用它来引用您创建的新对象。

为了能够合法地调用 f(),您需要做的是捕获 new 返回的指针并使用它来访问新对象

void B::mutate() {
  auto np = new (this) D2;    // reuses storage — ends the lifetime of *this
  f();              // undefined behavior**
  np->f()           // OK, np points to a valid object
  ... = this;       // OK, this points to valid memory
}