Virtual dispatching within a constructor/destructor

This post continues and explains the previous post.

In the example from the previous post, our base class has invoked a virtual function during the construction of the object, expecting that the correct function would be called. Why was that an error, you ask ?

The simple answer is this: You should never invoke virtual functions during the construction of objects, since they will not be properly dispatched through the vtable. The virtual table is actually not ready until construction is over, and the dispatch is essentially static while constructing, or destructing, the object. This phenomena is thoroughly discussed in an excerpt from Effective C++.

You may ask yourselves why would this be the desired behavior?
Let’s assume the dispatch was virtual, even within the constructor. Then, the following code would access an uninitialized value: B’s constructor first invokes A’s constructor, which invokes virtual f() before the member is initialized.

struct A {
    A () { f(); }
    virtual void f () { }
};

struct B : A {
        B () :member(0) {}
        void f () { std::cout << member; }
    private:
        int member;
};

int main () {
    B b;
    return 0;
}

More reasons justifying this behavior can be found in the aforementioned link, and with the help of google.

5 thoughts on “Virtual dispatching within a constructor/destructor

  1. This just shows how out of touch with C++ I’ve become.

    I’ve got a gut feeling that this could and should ‘work as expected’ (from a language design POV), but I’ve got to think about this some more.

    1. I’m glad to hear it got you thinking :)

      In Java (and Python?) this would work as you expected, but I am not entirely sure what is the correct behaviour to expect.

  2. Your blog post contains a number of typical… er… misconceptions that are often made when desribing the behavior of virtual calls made from constructor (or desructor). The main reason of these misconceptions is the use of popular “everday” terminology instead of the standard terminology. You describe the behavior in terms of “static” and “dynamic” dispatch, which, I would say, is the root of the confusion.

    In reality in standard C++ language there’s no such thing as “static” and “dynamic” dispatch. In C++ language the behavior of virtual function call is desribed by the following rule (not verbatim, but precise enough): the virtual function definition is taken from the dynamic type of the object used in the call. The notion of “dynamic type of the object” is the key notion that describes the behavior of virtual function.

    Now, in order to explain how virtual functions work when they are invoked from the constructor, all we need is to say that when a constructor (or destructor) of type `B` is working , the dynamic type of the object being constructed is assumed to be `B`. Period. That covers all cases and there’s no need to describe any other “dispatch specifics” here.

    Even if the above `B` is a base class subobject of some other, larger object of type `D`, the virtual function definitions from `D` are simply not considered, because, as I said above, as long as we are inside the constructor of `B`, the dynamic type of our object is assumed to be `B`, not `D`.

    Under the above rule, there’s no need (and really no point) to claim that the “dispatch” is “static”. It is completely irrelevant what kind of dispatch is used, as long as the dynamic type of the object is determined correctly.

    Moreover, the noton of static dispatch is utterly insufficient to fully describe the behavior in question. Consider, for example, the following silightly more complicated code

    struct Root {
       virtual void virtual_function() {}
       void foo() { virtual_function(); }
    };
    
    struct B : Root {
       void void virtual_function() {}
       B() { foo(); }
    };
    
    struct D : B {
       void void virtual_function() {}
    };
    
    int main() {
      D d;
    }
    

    Note that in this code sample, the constructor of `B` calls a virtual function indirectly: it calls `Root::foo` and `Root::foo` in turn calls a virtual function defined in all three classes. Which function is going to be called? The answer is `B::virtual_function`. Note, that even though the call is physically made from `Root` the dispatch goes not to `Root::virtual_function`, but to `B::virtual_function`. This is obviously a dynamic dispatch, but it dosn’t resolve to `D::virtual_function`, meaning that it is a “limited” dynamic dispatch. In the class hierarchy it only goes as low as `B`. This functionality is precisely what C++ language standard requires. How it is really implemented is just an implementation detail with little importance (there is plenty of information about it on the Net, VMT and the other bla-bla-bla).

    Now note, that if in the above example we called `virtual_function` directly from `B` constructor, the same dynamic dispatch mechanism would ensure that we’d execute `B::virtual_function`, exactly as required by the language specification. The point of all this is that there’s absolutely no need for any “static dispatch” here. Moreover, static dispatch alone cannot help you here to fully implement the requrements of C++ language specification, as illustrated by the example above.

    Finally, in reality when you make a direct call to a virtual function from a constructor, the compiler always knows the exact dynamic type of the object (it is `B` if you are inside the constructor of `B`). For this reason the compiler can optimize the call and perform static dispatch instead of dynamic dispatch. However, this is nothing tan just an optimization that has no effect on the program’s behavior. Again, static dipatch in case of direct call from the constructor is just an optimization. To use the popular concept of “static dispatch” to explain the language functionality in this case is not a good idea. It doesn’t really explain things, as it doesn’t explain the behavior with the indirect virtual call in my example above.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>