Joy of Programming: Calling Virtual Functions from Constructors

1
7024
Time to program

Time to program

Calling virtual functions from constructors is problematic, and this problem can manifest itself in many ways. In this column, we’ll take a look at this problem, with specific examples.

Last year, I bought a BlackBerry mobile. It came with software that can be installed on a PC, with which one can transfer songs, data, etc., from the PC to the mobile. When I installed the software and started it, it promptly crashed with the exception: “pure virtual function call”! Surprisingly, over a period of five years, I’ve faced the same problem many times, and some of the screenshots I’ve taken from different software are shown in Figures 1 to 3.

FireFoxPureVirtualCallError
Figure 1: FireFoxPureVirtualCallError

PureVirtCall
Figure 2: PureVirtCall

PureVirtCallAcrobat
Figure 3: PureVirtCallAcrobat

Note that this behaviour is not specific to Windows software; software compiled with GCC on Linux will fail with a similar exception. Now, let us dig deeper, to understand this software bug.

Virtual functions are resolved based on the runtime type of the object on which they are called. If we try invoking virtual methods from constructors or destructors, it is problematic. Why?

Consider the case of calling a virtual function from a constructor. When the base class constructor executes, the derived object is not constructed yet. If there is a virtual function call that is supposed to bind to the derived type, how can it be handled?

The ways in which OO languages handle this situation differ. In C++, the virtual function is treated as non-virtual, and the base type method is called. In Java, the call is resolved to the derived type. Both these approaches can cause unintuitive results. Let us first discuss the C++ case, and then move on to the Java case.

In C++, if you try the following program, it will print “Inside base::vfun” since the virtual function is resolved to the base type (i.e., static type itself, instead of the dynamic type):

struct base {
    base() {
        vfun();
    }
    virtual void vfun() {
        cout << "Inside base::vfunn";
    }
};
struct deri : base {
    virtual void vfun() {
        cout << "Inside deri::vfunn";
    }
};
int main(){
    deri d;
}

Now, how about this program:

struct base {
    base() {
        base * bptr = this;
        bptr->bar();
        // even simpler ...
        ((base*)(this))->bar();
    }
    virtual void bar() =0;
};
struct deri: base {
    void bar(){ }
};
int main() {
    deri d;
}

Now, you’ll get the “pure virtual function call” exception thrown by the C++ runtime, which is similar to the three screenshots we saw earlier! In this case, the bar() method is a pure virtual function, which means that it is not defined yet (it is defined later, in a derived class). However, since we are invoking bar() from the base class constructor, it tries to call the pure virtual function; it is not possible to invoke a function that is not defined, and hence it results in a runtime exception (technically, it is “undefined behaviour”).

Note how we invoked bar() in the base class constructor — it is after casting the this pointer into the (base *) type. If we attempt to directly call a pure virtual function, the compiler will give a compile-time error.

Now, let’s look at a simple Java example. Can you predict its output?

class Base {
    public Base() {
        foo();
    }
    public void foo() {
        System.out.println("In Base's foo ");
    }
}
class Derived extends Base {
    public Derived() {
        i = new Integer(10);    
    }
    public void foo() {
        System.out.println("In Derived's foo " + i.toString() );
    }
    private Integer i;
}
class Test {
    public static void main(String [] s) {
        new Derived().foo();
    }
}

The program crashes with a NullPointerException! Why? As I mentioned earlier, in Java, virtual functions are resolved to the dynamic type. Here, foo is a virtual function (in Java, all non-static, non-final methods are virtual) and we try invoking it from the constructor. Since it resolves to the dynamic type, the derived version of foo is called.

Remember that we are still executing the base class constructor, and the derived constructor is yet to execute. Hence the private variable i inside Derived is not initialised yet (and all reference type variables are initialised to null in Java). Hence, the call i.toString() results in accessing the yet-to-be-initialised Derived object, and results in a NullPointerException.

The C# approach to calling virtual functions is similar to that of Java.

Calling virtual functions from constructors/destructors is risky, no matter which OO language we use. Even if the program works in cases where virtual functions are called from constructors, the program can suddenly start failing if we extend the base classes in which such calls are present. Hence, it is a bad programming practice to call virtual functions from constructors/destructors, and most static analysers warn about this problem.

Feature image courtesy: Dan Foy. Reused under the terms of CC-BY 2.0 License.

1 COMMENT

  1. Ganesh,
    In your example: you can call the pure virtual function defined in base class in its constructor by ‘
    this->bar();
    Provided you have defined it.
    Whereas, if you call using the cast operator (which is there in your code), it will crash.
    -Cheers,
    Siddhartha
     

LEAVE A REPLY

Please enter your comment!
Please enter your name here