Behavior of virtual functions in constructors – C++

Posted: December 2, 2010 in C++
Tags: , , , ,

The hierarchy of constructor calls brings up an interesting dilemma. What happens if you’re inside a constructor and you call a virtual function? Inside an ordinary member function you can imagine what will happen – the virtual call is resolved at runtime because the object cannot know whether it belongs to the class the member function is in, or some class derived from it. For consistency, you might think this is what should happen inside constructors.This is not the case. If you call a virtual function inside a constructor, only the local version of the function is used. That is, the virtual mechanism doesn’t work within the constructor.

This behavior makes sense for two reasons. Conceptually, the constructor’s job is to bring the object into existence (which is hardly an ordinary feat). Inside any constructor, the object may only be partially formed – you can only know that the base-class objects have been initialized, but you cannot know which classes are inherited from you. A virtual function call, however, reaches “forward” or “outward” into the inheritance hierarchy. It calls a function in a derived class. If you could do this inside a constructor, you’d be calling a function that might manipulate members that hadn’t been initialized yet, a sure recipe for disaster.

The second reason is a mechanical one. When a constructor is called, one of the first things it does is initialize its VPTR. However, it can only know that it is of the “current” type. The constructor code is completely ignorant of whether or not the object is in the base of another class. When the compiler generates code for that constructor, it generates code for a constructor of that class, not a base class and not a class derived from it (because a class can’t know who inherits it). So the VPTR it uses must be for the VTABLE of that class. The VPTR remains initialized to that VTABLE for the rest of the object’s lifetime this isn’t the last constructor call. If a more-derived constructor is called afterwards, that constructor sets the VPTR to VTABLE, and so on, until the last constructor finishes. The state of the VPTR is determined by the constructor that is called last. This is another reason why the constructors are called in order from base to most-derived.

But while all this series of constructor calls is taking place, each constructor has set the VPTR to its own VTABLE. If it uses the virtual mechanism for function calls, it will produce only a call through its own VTABLE, not the most-derived VTABLE (as would be the case after the constructors were called). In addition, many compilers recognize that a virtual function call is being made inside a constructor, and perform early binding because they know that late-binding will produce a call only to the local function. In either event, you won’t get the results you might expect from a virtual function call inside a constructor.

class Base
{
public:
    Base()
    {
        DoSomething();
    }

    virtual ~Base()
    {
    }

    // This will be overridden in the derived type.
    virtual void DoSomething()
    {
        printf( "Base::DoSomething\r\n" );
    }
};

class DerivedType : public Base
{
public:
    DerivedType ()
    {
    }

    ~DerivedType ()
    {
    }

    void DoSomething()
    {
        printf( "DerivedType::DoSomething\r\n" );
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Base *pDerivedType = new DerivedType();
    // Code here
    if( NULL != pDerivedType )
    {
        delete pDerivedType;
        pDerivedType = 0;
    }
    return 0;
}

Output:
Base::DoSomething
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s