9

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?

Community
  • 1
  • 1
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • Wouldn't invalidating pointers completely defeat the purpose of placement new? – Luc Touraille Sep 20 '12 at 12:55
  • 2
    Why is that function virtual? That's a bit scary. – Kerrek SB Sep 20 '12 at 12:56
  • 1
    Also, if the constructor of `Class` throws in exception, you're in an ethical dilemma. (And *please* don't call your class "Class"...) – Kerrek SB Sep 20 '12 at 12:57
  • @Luc Touraille: Well, I'm not aware of sane cases where placement new is called to re-create the object at the same location. – sharptooth Sep 20 '12 at 13:06
  • Are you concerned specifically by the fact that the replacement is done from within a method of the object, moreover a virtual method, or could we replace your sample code by a simpler one such as this one: `struct T {}; T * obj = new T; obj->~T(); new( obj ) T;`? – Luc Touraille Sep 20 '12 at 13:16
  • @Luc Touraille: The main concern is that the pointer in question has no "backwards" connection from the object, so it should be a member function or a free function or another pointer variable storing the same address. – sharptooth Sep 20 '12 at 13:23
  • @sharptoots, standard also covers that :) `Such use of explicit placement and destruction of objects can be necessary to cope with dedicated hardware resources and for writing memory management facilities.` – Zdeslav Vojkovic Sep 20 '12 at 13:36

6 Answers6

8

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;
}
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • 2
    Possibly the most onerous of the various requirements is that the object must not have any `const` or reference data members (and neither can its members have, since when you apply this trick to an object you're applying the same trick to all its members and their members etc). Such data members are probably the main reason that would cause someone to think, "I can't mutate the object so I need to destroy and re-create it", but they'd be wrong. – Steve Jessop Sep 20 '12 at 14:56
  • 2
    So tempting to downvote for non-exception-safe self-assignment-checking assignment operator... but then I read it was the example from the Standard. -1 to the Standard. – Puppy Sep 24 '12 at 17:35
4

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.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Your remarks about potential UB are valuable, but you don't really provide any arguments backing up your claim that "this is fine". Could you elaborate? – Luc Touraille Sep 20 '12 at 13:18
  • There's a Standard quote explicitly describing the practice as legal. Specifically, pointers are not invalid as long as there is an object of the right type at the right location- and how it got there is not their problem, as it were. – Puppy Sep 24 '12 at 17:35
4

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 :)

Zdeslav Vojkovic
  • 14,391
  • 32
  • 45
  • Strictly speaking, calling a destructor does not "clean up internal state", it actually *destroys* the object (according to the standard terminology (§12.4)). However, your remark about pointers being invalidated only when memory is deallocated seems true (see §3.7.4.2). Even if there is no longer any object at the memory pointed to by the pointer, the memory is still allocated so the pointer is still valid (at least, this is my interpretation of the standard). – Luc Touraille Sep 20 '12 at 13:09
  • You are right, but I was mostly describing what really happens under the hood - I know quite a few people who think that deallocation is somehow tied to the destructor method. I have clarified the answer. – Zdeslav Vojkovic Sep 20 '12 at 13:29
0

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.

ypnos
  • 50,202
  • 14
  • 95
  • 141
  • What on earth does `realloc` have to do with anything? – Puppy Sep 20 '12 at 12:55
  • Nothing, I just wanted to remind that sometimes addresses change. And that's the only reason why a pointer may become invalid, obviously. In this scenario, not. – ypnos Sep 20 '12 at 14:01
0

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.

-2

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.

Suncat2000
  • 1,105
  • 1
  • 14
  • 17
  • 3
    Also incorrect. A pointer only has to point to *some* valid object (of the correct type) at the time of de-referencing to be valid. Pointers and references which were valid before `method` are valid after `method`. – Puppy Sep 20 '12 at 13:00
  • @DeadMG: Your comment is incorrect. The object may not be of the correct type and that makes using an existing pointer unpredictable. The pointer can be dereferenced, but that doesn't make it valid. If the new object is of a correct type, it makes things confusing and program behavior unreliable. A "-1" on my answer is uncalled for. – Suncat2000 Sep 21 '12 at 14:36
  • It is completely called for. If he reconstructs the correct type, the behaviour is well-defined. The fact that it's difficult to debug is certainly true- but it's *not* true that pointers to the old object are invalid. – Puppy Sep 24 '12 at 17:34
  • My answer is correct. It _is_ true that pointers to the old object are invalid _because there is no old object_! Though the pointer can certainly be dereferenced _if_ it points to a correctly typed _new_ object, the outcome of an operation that depends on this data will not be correct and, therefore, _invalid_. You may dislike my answer, but respectfully, please remove your downvote. – Suncat2000 Sep 25 '12 at 19:06