Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

reusing object's space by another object

I was looking to re-use allocated space within the base class from this pointer and C++ Standard does not approve. However, the wording of the standard seems to be wrong. It puts a condition "and before the storage which the object occupied is reused or released", but it is clearly reused in their own code snippet. Where I am getting it wrong?

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
    }
like image 691
minex Avatar asked Sep 18 '25 12:09

minex


2 Answers

When you do

f();

in a member function what you are really doing is

this->f();

So in the example when they do

new (this) D2; 

it ends the lifetime of the thing pointer to by this and creates a new D2 in it's place. That makes you think that this->f(); is okay since this now points to an object that has had it's lifetime started but you are forgetting that this is a pointer to an object that has had it's lifetime ended. You can't use it to refer to the new object that you made.

In order to be able to call f() legally what you would need to do is capture the pointer returned by new and use it to access the new object

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
}
like image 156
NathanOliver Avatar answered Sep 20 '25 05:09

NathanOliver


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

Correct. Because the stoarge has been reused, the "Otherwise" clause applies:

... 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.

Calling f() through a void* is not possible, so it is not allowed by that clause. Otherwise, calling member functions of an object whose lifetime has ended is undefined (outside of the destructor).

... = this; on the other hand is something that can be done with a void*.

Note that (new (this) D2)->f() would be well-defined.

like image 30
eerorika Avatar answered Sep 20 '25 03:09

eerorika