This is a follow-up to this question. Suppose I have this code:

class Class {
    public virtual method()
    {
        this->~Class();
        new( this ) Class();
    }
};

Class* object = new Class();
object->method();
delete object;

which is a simplified version of what this answer suggests.

Now once a destructor is invoked from within method() the object lifetime ends and the pointer variable object in the calling code becomes invalid. Then the new object gets created at the same location.

Does this make the pointer to the object in the calling valid again?

有帮助吗?

解决方案

This is explicitly approved in 3.8:7:

3.8 Object lifetime [basic.life]

7 - If, after the lifetime of an object has ended [...], a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object [...] can be used to manipulate the new object, if: (various requirements which are satisfied in this case)

The example given is:

struct C {
  int i;
  void f();
  const C& operator=( const C& );
};
const C& C::operator=( const C& other) {
  if ( this != &other ) {
    this->~C(); // lifetime of *this ends
    new (this) C(other); // new object of type C created
    f(); // well-defined
  }
  return *this;
}

其他提示

Strictly, this is fine. However, without extreme care, it will become a hideous piece of UB. For example, any derived classes calling this method won't get the right type re-constructed- or what happens if Class() throws an exception. Furthermore, this doesn't really accomplish anything.

It's not strictly UB, but is a giant pile of crap and fail and should be burned on sight.

The object pointer doesn't become invalid at any time (assuming your destructor doesn't call delete this). Your object was never deallocated, it has only called it's destructor, i.e. it has cleaned up its internal state (with regard to implementation, please note that standard strictly defines that the object is destroyed after destructor call). As you have used placement new to instantiate the new object at the exactly same address, it is technically ok.

This exact scenario is covered by section 3.8.7 of C++ standard:

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

That said, this is interesting only as learning code, as production code, this is horrible :)

The pointer only knows its address and as soon as you can confirm that the address of the new object is the one the pointer is pointing to, the answer is yes.

There are some cases where people would believe the address does not change, but in some cases it does change, e.g. when using C's realloc(). But that's another story.

You may want to reconsider explicitly calling the destructor. If there's code you want executed that happens to be in the destructor, move that code to a new method and call that method from the destructor to preserve the current functionality. The destructor is really meant to be used for objects going out of scope.

Creating a new object at the location of a destroyed one does not make any pointers valid again. They may point to a valid new object, but not the object you were originally referencing.

You should guarantee that all references were removed or somehow marked as invalid before destroying the original object.

This would be a particularly difficult situation to debug.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top