Thursday, May 17, 2007

Comparisons of C++ and Java (I): Static Binding and Dynamic Binding in C++

After years of Java programming, I have to pick up C++ for my work. Sometimes, it is hard to switch from Java thinking. Here is one of the example: Static binding and Dynamic binding in C++.

Let me start with 3 examples.

Example 1 (In Java):

base class:

public class base{
public base(){}
public void printMsg(){
System.out.println("I am printMsg() from base class");
}

derived class

public class derived extends base{
public derived(){}
public void printMsg(){
System.out.println("I am printMsg() from derived class")
}
)

A test class:

public class test {
public static void main(String[] args){
base myClass = new derived();
myClass.printMsg();
}
}

What do get? Here we go:

>>I am printMsg() from derived class

It is so obvious to a Java programmer. OK, let’s look at C++ version of the example above.

Example 2 (in C++): this example looks exactly same as example 1 but in C++.

base class:

class base{
public:
base(){};
void printMsg( cout<<”hi, printMsg() from base”<<endl;);
};
...

derived class:


class derived : public base {
public:
derived();
void printMsg(cout<<”hi, printMsg() from derived class”<<endl);
};
...
test class snippet

....
base* myClass = new Polygon::derived();
myClass->printMsg();
...

What do we get? There we go:

>>hi, printMsg() from base

and not “hi, printMsg() from derived class” as I expected.

How can I access derived class version of printMsg() in C++? There is a way out: by using pure virtual methods. In base.cc we change declaration of void printMsg() as:

Example 3 (in C++): This example is same as example 2 except in the base class. The method printMsg() is made pure virtual:


class base{
public:
base(){};
void printMsg( cout<<”hi, printMsg() from base”<<endl;);
virtual void printMsg() = 0;
};

Then compile and run, I get what I want:

>>hi, printMsg() from derived class

The derived class version of printMsg() get called.

What is going on here?

It is because Java and C++ provide different binding mechanisms to achieve run-time polymorphism.

In C++ there are two kinds of data binding: static binding and dynamic binding. Static binding is by means of nonpuer virtual functions while dynamic binding is by virtual functions.
At run time, the implementation is chosen differently. In static binding, decision is made by the static type of pointer or reference, while in dynamic binding, decision is made by the actual type of object being pointed to.

This can explain the results from example 2 and 3. In example 2, base class contains no virtual printMsg() function, so it is static binding. In example 3, method printMsg() is a virtual function, so dynamic binding applies, therefore the derived class version of printMsg() is picked up.

What happens in Java? In Java, every method is treated as pure virtual method and dynamic binding applies always.

Extra note on pure virtual method in C++:

  1. When using pure virtual methods in C++, never try to call a pure virtual method from a constructor or destructor. Compiler will complain “error: abstract virtual `virtual method' called from constructor” at compilation time.
  2. Making destructor virtual is a best practice since it would make sure the destructor in derived class get called too as desired.

3 comments:

Brian said...

Pure Virtual means that it's virtual and has no implementation (the same as abstract in other languages).

The function only has to be declared as virtual (not pure-virtual) for dynamic binding to succeed.

Anonymous said...

You'll be lucky if the compiler complains about a virtual being called in a ctor/dtor. I do not believe that they are required to issue a diagnostic, but the good ones will.

Calling a virtual in a ctor/dtor is one of those things that you Just Don't Do (tm).

And take care when recommending making destructors virtual by default. Value semantics and "concrete" types are valued highly in C++.

IMHO, you should think in terms of value types by default in C++, which is a change in mind-set when coming from Java. Certainly that's the approach taken in the standard library and it works very well.

Copy construction and copy-assignment breaks down for classes with virtual functions once you start deriving from them, meaning they won't work correctly with standard containers etc unless you start using pointers to them.

In fact the difference in the generics systems between C++ and Java highlight the value/reference semantic divide.

I'd recommend only using virtuals in a base class for hierarchy designed to take advantage of dynamic binding/dispatch. There are other types of base class where you wouldn't want to use virtuals.

Xiaoyun Tang said...

Thanks Brian, you are right. The function only has to be virtual and not necessarily pure virtual.