6

Question

What does the C++ standard guarantee about the state of an object in the time after a derived class's destructor executes, but before the base class's destructor executes ? (This is the time when the derived class's subobjects' destructors are being called.)

Example

#include <string>
struct Base;

struct Member {
  Member(Base *b);
  ~Member();
  Base *b_;
};

struct Base {
  virtual void f() {}
  virtual ~Base() {}
};

struct Derived : Base {
  Derived() : m(this) {}
  virtual ~Derived() {}  
  virtual void f() {}
  std::string s; 
  Member m;
};

Member::Member(Base *b) : b_(b) {}
Member::~Member() {
  // At this point, ~Derived has finished -- can we use b_ as a 
  // Derived* object (i.e. call Derived::f or access Derived::s)?
  b_->f();
}

int main() {
  Base *bd = new Derived;
  delete bd;
}

In this example, a Member object has a pointer to a Derived object that owns it, and it attempts to access that Derived object as it is destructed...even though the destructor for Derived has already finished.

Which version of *bd's virtual functions would be called if some subobject called a virtual function after ~Derived() executes, but before ~Base() executes? Is it even legal to access *bd when it's in that state?

Nate Kohl
  • 35,264
  • 10
  • 43
  • 55

3 Answers3

5

For me, from [12.4] it clearly stands, that it is not legal:

Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended (3.8). [Example: ...]

Despite the lack of definition for no longer exists, I think, I can say that referencing an object which no longer exists results in undefined behaviour.

Rafał Rawicki
  • 22,324
  • 5
  • 59
  • 79
  • That quote looks promising, but it looks (to me) more like it's saying that running a destructor twice for the same object is UB. – Nate Kohl Jun 27 '12 at 22:11
  • @NateKohl I've bolded the relevant part of the quote, the rest can be omitted. – Rafał Rawicki Jun 27 '12 at 22:13
  • As for the "no longer exists", [3.8] says: "The lifetime of an object of type T ends when: — if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or — the storage which the object occupies is reused or released" – Rafał Rawicki Jun 27 '12 at 22:15
  • After the destructor starts, the lifetime of an object may be over, but you can still interact with the object -- i.e. the body of the destructor can do all sorts of things. That makes the concept of "object lifetime" a little unclear to me... – Nate Kohl Jun 27 '12 at 22:17
  • I agree and there is no other place in C++11 standard to clarify this matter. – Rafał Rawicki Jun 27 '12 at 22:20
  • Those quotes do seem to be close to an answer, though. :) – Nate Kohl Jun 27 '12 at 22:21
1

At the moment in time that you're calling b_->f(), Derived is in the process of being destructed, but Base hasn't been destructed yet. You're still in limbo though, because it's Derived::f() that you're going to be calling.

Edit:

Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor’s own class or in one of its bases, but not a function overriding it in a class derived from the constructor or destructor’s class, or overriding it in one of the other base classes of the most derived object (1.8). If the virtual function call uses an explicit class member access (5.2.5) and the object-expression refers to the object under construction or destruction but its type is neither the constructor or destructor’s own class or one of its bases, the result of the call is undefined.

C++0x working draft Sec 12.7 par 4

Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
  • So my question really is: how much do we know about that state of limbo? Members are destructed in reverse order of construction, so the string member should still be around, right? Do we know when the vtable pointer will be updated? – Nate Kohl Jun 27 '12 at 22:06
  • Hmm...that quote looks like it's talking about what happens *during* a destructor call, but not necessarily about what happens *after* the call. – Nate Kohl Jun 27 '12 at 22:33
  • @NateKohl: Am I correct that you're asking specifically which version of an object's virtual functions would be called if some subobject called a virtual function after `~Derived()` executes, but before `~Base()` executes (this is the time when `Derived`'s subobject's destructors are being called)? – Ken Bloom Jun 27 '12 at 22:43
  • Yes; and more generally, what else (if anything) can we assume about the `Derived` object when it's in that state? – Nate Kohl Jun 27 '12 at 22:52
  • @NateKohl: I have completely rewritten your question to address this. – Ken Bloom Jun 27 '12 at 23:04
  • @NateKohl: I didn't see any specific language addressing this in the standard, but maybe I didn't look hard enough. – Ken Bloom Jun 27 '12 at 23:05
  • Thanks for the edits; I think they've made the question clearer. – Nate Kohl Jun 27 '12 at 23:13
1

from n3290 [class.cdtor]

12.7 Construction and destruction 1 For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior. For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.

  • This might be the closest answer yet, but it's still a little vague about when a destructor finishes execution: 12.4 says "After executing the body of the destructor...a destructor for class `X` calls the destructors for `X`'s direct non-variant non-static data members...", perhaps implying that a destructor isn't done until it finishes destructing its members? – Nate Kohl Jun 28 '12 at 15:42
  • Do not think so. I think that paragraph 2.7 is clear. After the execution of the destructor, comes the call to other destructors (2.4), but by this time we are in undefined behavior land (2.7). The lifetime of the object, by definition, ended when destructor starts, but during the execution of the dtor, it is valid to refer to members. – Fernando Pelliccioni Jun 28 '12 at 23:24
  • @FernandoPelliccioni "_The lifetime of the object, by definition, ended when destructor starts_" then "lifetime" is not a useful concept. – curiousguy Jul 24 '12 at 19:59
  • @curiousguy I don't undestand your point. Could you elaborate? – Fernando Pelliccioni Nov 11 '13 at 01:20
  • @FernandoPelliccioni If lifetime means "until the destructor starts", then how is it a useful concept? What property of an object is true only until the destructor starts? – curiousguy Nov 11 '13 at 06:22
  • @curiousguy "how is it a useful concept?" You may like it or not, but it is a design decision and it is a well-defined and standardized behavior. In other programming languages (especially in garbage-collected) the concept doesn't exist or hasn't been thought. Not to mention the about standardization, I believe that these languages ​​are poorly standardized and leave many important concepts without specifying. I think it's an important concept, one should know when a piece of memory begins to have meaning (construction) and when it ceases to have it (destruction). – Fernando Pelliccioni Nov 11 '13 at 16:06
  • @curiousguy (continuation) "What property of an object is true only until the destructor starts?" I don't understand this question. What do you mean by "property of an object"? – Fernando Pelliccioni Nov 11 '13 at 16:07
  • @FernandoPelliccioni You may like it or not, but lifetime is an empty, useless "concept". It does not correspond to anything. Its definition is without interest. You cannot use lifetime in any meaningful way. – curiousguy Nov 11 '13 at 20:48
  • "_You may like it or not, but it is a design decision and it is a well-defined and standardized behavior._" Which behavior are you talking about? There is no behavior implied by end of lifetime, which is my whole point. The concept is useless because **nothing happens with a destructor is entered.** This is standard behavior! – curiousguy Nov 11 '13 at 20:53
  • @curiousguy: If you think that the C++ language designers are fools and they described a concept useless, well :\ Could it not be that they do not know what to do with their time? Better continue programming in Java. Sarcasm mode off. I recommend you read the standard, especially [basic.life] ...or maybe this example helps http://pastebin.com/Ej7CUNFj – Fernando Pelliccioni Nov 12 '13 at 01:01
  • I fell into the trap. Troll in sight. http://chat.stackoverflow.com/transcript/message/4811542 – Fernando Pelliccioni Nov 12 '13 at 01:13
  • Please don't accuse me of trolling, as it is insulting. – curiousguy Nov 12 '13 at 01:16
  • I am one of these "C++ language designers". I am not a fool. Still, the C++ standard contains a bunch of non sense. Java? Please... You still have not explained how the concept of lifetime, as defined in the C++ standard, is relevant to the definition of C++ or useful in any way. – curiousguy Nov 12 '13 at 01:17
  • Oh pleeeease. [basic.life] is a bunch of crap. "_The properties ascribed to objects throughout this International Standard apply for a given object only during its lifetime._" is WRONG. NOTHING stops being valid as soon as you enter the dtor. NOTHING. This section is moronic, and I feel sorry for you that you worship the standard as religious sects follow the "Holly Bible". – curiousguy Nov 12 '13 at 01:22
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/40986/discussion-between-curiousguy-and-fernando-pelliccioni) – curiousguy Nov 12 '13 at 01:23