Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>As already mentioned by the other two answers, the type of an object changes as the destructors are being executed. Once a destructor completes, the object of that type does no longer exist, and only it's base subobjects exists (until their destructors complete).</p> <p>The reason for this answer is proposing an interesting experiment, what will the output of this code be? (Oh, well, all three answers already told you, but the experiment is interesting in itself):</p> <pre><code>#include &lt;iostream&gt; struct base { static void print_type( base const &amp; b ) { // [1] std::cout &lt;&lt; b.type() &lt;&lt; std::endl; } virtual std::string type() const { // [2] return "base"; } virtual ~base() { print_type( *this ); } base() { print_type( *this ); } }; struct derived : base { std::string type() const { return "derived"; } ~derived() { print_type( *this ); } derived() { print_type( *this ); } }; struct most_derived : derived { std::string type() const { return "most_derived"; } ~most_derived() { print_type( *this ); } most_derived() { print_type( *this ); } }; int main() { most_derived md; base::print_type( md ); } </code></pre> <p>Notes:</p> <p>For extra fun, calls to <code>print_type</code> are also added in the constructor. The function serves as verification of the dynamic type of the object at that particular point in time. The function <code>print_type</code> (that could be a freestanding function, and implemented in a different translation unit --as to avoid the compiler from <em>seeing</em> inside it). While compiling the function, the compiler cannot know whether it is called from inside a constructor, destructor, or outside of any of them, so the generated code <em>must</em> use the dynamic dispatch mechanism, and will be dispatched to the <em>final overrider</em> at each point in time.</p> <p>As to the validity of the code is guaranteed by §12.7/2:</p> <blockquote> <p>To explicitly or implicitly convert a pointer (an lvalue) referring to an object of class X to a pointer (reference) to a direct or indirect base class B of X, the construction of X and the construction of all of its direct or indirect bases that directly or indirectly derive from B shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior. To form a pointer to (or access the value of) a direct nonstatic member of an object obj, the construction of obj shall have started and its destruction shall not have completed, otherwise the computation of the pointer value (or accessing the member value) results in undefined behavior.</p> </blockquote> <p>Conversions to <code>base&amp;</code> on the call to <code>print_type</code> are valid as they are performed after the construction of <em>each</em> object has started, and before the destruction of <em>each</em> object has completed (<em>each</em> refers to each one of the subobjects of <code>most_derived</code> in the program).</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload