views:

524

answers:

3

I tried calling an overridden method from a constructor of a parent class and noticed different behavior across languages.

C++ - echoes A.foo()

class A{

public: 

    A(){foo();}

    virtual void foo(){cout<<"A.foo()";}
};

class B : public A{

public:

    B(){}

    void foo(){cout<<"B.foo()";}
};

int main(){

    B *b = new B(); 
}

Java - echoes B.foo()

class A{

    public A(){foo();}

    public void foo(){System.out.println("A.foo()");}
}

class B extends A{  

    public void foo(){System.out.println("B.foo()");}
}

class Demo{

    public static void main(String args[]){
        B b = new B();
    }
}

C# - echoes B.foo()

class A{

    public A(){foo();}

    public virtual void foo(){Console.WriteLine("A.foo()");}
}

class B : A{    

    public override void foo(){Console.WriteLine("B.foo()");}
}


class MainClass
{
    public static void Main (string[] args)
    {
        B b = new B();              
    }
}

I realize that in C++ objects are created from top-most parent going down the hierarchy, so when the constructor calls the overridden method, B does not even exist, so it calls the A' version of the method. However, I am not sure why I am getting different behavior in Java and C# (from C++)

+15  A: 

In C++, as you correctly noted, the object is of type A until the A constructor is finished. The object actually changes type during its construction. This is why the vtable of the A class is used, so A::foo() gets called instead of B::foo().

In Java and C#, the vtable (or equivalent mechanism) of the most-derived type is used throughout, even during construction of the base classes. So in these languages, B.foo() gets called.

Note that it is generally not recommended to call a virtual method from the constructor. If you're not very careful, the virtual method might assume that the object is fully constructed, even though that is not the case. In Java, where every method is implicitly virtual, you have no choice.

Thomas
You can mark methods as `final` in Java, which prevents them from being overridden by subclasses. I believe there is a similar mechanism in C# (perhaps `sealed`?).
Michael Myers
Yep, marking methods that are (directly or indirectly) called from the constructor as `final` would be wise. There is no need for a similar mechanism in C# because in C#, methods are only `virtual` if you explicitly declare them so.
Thomas
"In Java and C#, the vtable (or equivalent mechanism) of the most-derived type is used throughout" - could you please elucidate, I know what is a vtable though.
Vaibhav Bajpai
+4  A: 

While I understand that you're doing this for experiments, it's important to note the following quote from Effective Java 2nd Edition, Item 17: Design and document for inheritance, or else prohibit it:

There are a few more restrictions that a class must obey to allow inheritance. Constructors must not invoke overridable methods, directly or indirectly. If you violate this rule, program failure will result. The superclass constructor runs before the subclass constructor, so the overriding method in the subclass will be invoked before the subclass constructor has run. If the overriding method depends on any initialization performed by the subclass constructor, the method will not behave as expected.

Here's an example to illustrate:

public class ConstructorCallsOverride {
    public static void main(String[] args) {
        abstract class Base {
            Base() { overrideMe(); }
            abstract void overrideMe(); 
        }
        class Child extends Base {
            final int x;
            Child(int x) { this.x = x; }
            @Override void overrideMe() {
                System.out.println(x);
            }
        }
        new Child(42); // prints "0"
    }
}

Here, when Base constructor calls overrideMe, Child has not finished initializing the final int x, and the method gets the wrong value. This will almost certainly lead to bugs and errors.

polygenelubricants
"so the overriding method in the subclass will be invoked" - how can superclass during its construction itself even invoke a subclass method (when subclass is yet to be constructed)? (I understood the example above that subclass initialization was not done, but I was hoping the method should have not even been called, because the subclass is yet to be constructed)
Vaibhav Bajpai
A: 

Is there a way of forcing C++ to call B::foo() in the above example?

oOoO