8

On the last line of a destructor, I have a diagnostic type message which takes a printf-like form:

"object destroyed at %p", this

I have concerns though about how well this is defined at such a point.

Should I have such reservations? Is the behaviour well-defined?

P45 Imminent
  • 8,319
  • 4
  • 35
  • 78
  • it'll get destroyed when it returns from the destructor so it should still be valid – EdChum Sep 03 '14 at 07:59
  • *should* or *is*? I'll be in a lot of trouble if I introduce UB. – P45 Imminent Sep 03 '14 at 07:59
  • Is valid, you could just test this yourself – EdChum Sep 03 '14 at 08:00
  • 3
    On my platform indeed I could. I could test `i = ++i` at the same time. But that is no guide at all to *defined* behaviour. – P45 Imminent Sep 03 '14 at 08:00
  • It would depend on whether the destructor is `non-trivial` see related: http://stackoverflow.com/questions/8611468/lifetime-of-object-is-over-before-destructor-is-called. In the case of normal destructor the object lifetime ends when you return from the destructor. If it happened once you enter the body then you wouldn't be able to release member resources – EdChum Sep 03 '14 at 08:04
  • Reopened - the linked "duplicate" wondered whether you can call functions using `this`, whereas this is much more restricted. In particular, no assumption is made here about members still existing, just the memory. – MSalters Sep 03 '14 at 09:04

5 Answers5

9

According to the C++ Standard (12.4 Destructors)

8 After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X’s direct non-variant non-static data members, the destructors for X’s direct base classes and, if X is the type of the most derived class (12.6.2), its destructor calls the destructors for X’s virtual base classes.

So your code is well-formed. All destructors of non-static data members and base classes are called after executing the body of the destructor.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
2

Well, the pointer itself certainly still exists (it's just an address, after all). There should be no problem to print the pointer value.

On the other hand, everything that you did in the destructor has already happened. Attributes may already have been delete'd, etc, so you have to avoid anything that accesses those.

Sjlver
  • 1,227
  • 1
  • 12
  • 28
  • +1, the core question here is whether the pointer still has a meaningful value, not whether it can be dereferenced. – MSalters Sep 03 '14 at 09:07
1

This has perfectly well defined behaviour. Consider that the this pointer can be used implicitly or explicitly throughout the destructor, e.g. whenever you access a member variable for things like delete ptr_;. After the destructor returns, the members are destroyed in reverse order of declaration/creation then the base destructors invoked.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
0

As you might now, you can access your class members from the destructor. This would not be working if the this pointer was invalid. So you can safely assume that the address this points to is still the same that you might have printed in the constructor.

oxygene
  • 621
  • 4
  • 14
0

Inside the destructor the this pointer is well defined, as are all the members and bases (that will be destroyed in construction reverse order after the destructor return). So printing the address it refers is not UB.

The only thing is that the object itself cannot be assumed anymore as "polymorphic", since the derived components had already been destroyed.

class A
{
public:
    virtual void fn() { std::cout << "A::fn" << std::endl; }
    virtual ~A() { fn(); } //< will call A::fn(), even if B is destroying
};

class B: public A
{
public:
    virtual void fn() { std::cout << "B::fn" << std::endl; }
    virtual ~B() {}    
};

int main()
{
    B b;
    A& a = b;
    a.fn(); //< will print B::fn(), being A::fn virtual and being B the runtime-type of the a's referred object
    return 0; //< will print A::fn() from b's A's component destructor
}
SCFrench
  • 8,244
  • 2
  • 31
  • 61
Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63