Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invoking virtual method in constructor: difference between Java and C++

In Java:

class Base {
    public Base() { System.out.println("Base::Base()"); virt(); }
    void virt()   { System.out.println("Base::virt()"); }
}

class Derived extends Base {
    public Derived() { System.out.println("Derived::Derived()"); virt(); }
    void virt()      { System.out.println("Derived::virt()"); }
}

public class Main {
    public static void main(String[] args) {
        new Derived();
    }
}

This will output

Base::Base()
Derived::virt()
Derived::Derived()
Derived::virt()

However, in C++ the result is different:

Base::Base()
Base::virt() // ← Not Derived::virt()
Derived::Derived()
Derived::virt()

(See http://www.parashift.com/c++-faq-lite/calling-virtuals-from-ctors.html for C++ code)

What causes such a difference between Java and C++? Is it the time when vtable is initialized?

EDIT: I do understand Java and C++ mechanisms. What I want to know is the insights behind this design decision.

like image 442
Xiao Jia Avatar asked Nov 18 '12 13:11

Xiao Jia


People also ask

Can we call virtual function in constructor in Java?

Calling virtual methods in constructor/destructor in C++You can call a virtual function in a constructor. The Objects are constructed from the base up, “base before derived”.

Can constructor call virtual method?

You can call a virtual function in a constructor, but be careful. It may not do what you expect. In a constructor, the virtual call mechanism is disabled because overriding from derived classes hasn't yet happened. Objects are constructed from the base up, “base before derived”.

Do not invoke virtual functions from constructors or destructors?

As a general rule, you should never call virtual functions in constructors or destructors. If you do, those calls will never go to a more derived class than the currently executing constructor or destructor. In other words, during construction and destruction, virtual functions aren't virtual.

What is virtual method invocation in Java?

Virtual method invocation is the invocation of the correct overridden method, which is based on the type of the object referred to by an object reference and not by the object reference itself. It's determined at runtime, not at compilation time.


2 Answers

Both approaches clearly have disadvatages:

  • In Java, the call goes to a method which cannot use this properly because its members haven’t been initialised yet.
  • In C++, an unintuitive method (i.e. not the one in the derived class) is called if you don’t know how C++ constructs classes.

Why each language does what it does is an open question but both probably claim to be the “safer” option: C++’s way prevents the use of uninitialsed members; Java’s approach allows polymorphic semantics (to some extent) inside a class’ constructor (which is a perfectly valid use-case).

like image 144
Konrad Rudolph Avatar answered Oct 10 '22 15:10

Konrad Rudolph


Well you have already linked to the FAQ's discussion, but that’s mainly problem-oriented, not going into the rationales, the why.

In short, it’s for type safety.

This is one of the few cases where C++ beats Java and C# on type safety. ;-)

When you create a class A, in C++ you can let each A constructor initialize the new instance so that all common assumptions about its state, called the class invariant, hold. For example, part of a class invariant can be that a pointer member points to some dynamically allocated memory. When each publicly available method preserves the class invariant, then it’s guaranteed to hold also on entry to each method, which greatly simplifies things – at least for a well-chosen class invariant!

No further checking is then necessary in each method.

In contrast, using two-phase initialization such as in Microsoft's MFC and ATL libraries you can never be quite sure whether everything has been properly initialized when a method (non-static member function) is called. This is very similar to Java and C#, except that in those languages the lack of class invariant guarantees comes from these languages merely enabling but not actively supporting the concept of a class invariant. In short, Java and C# virtual methods called from a base class constructor can be called down on a derived instance that has not yet been initialized, where the (derived) class invariant has not yet been established!

So, this C++ language support for class invariants is really great, helping do away with a lot of checking and a lot of frustrating perplexing bugs.

However, it makes a bit difficult to do derived class specific initialization in a base class constructor, e.g. doing general things in a topmost GUI Widget class’ constructor.

The FAQ item “Okay, but is there a way to simulate that behavior as if dynamic binding worked on the this object within my base class's constructor?” goes a little into that.

For a more full treatment of the most common case, see also my blog article “How to avoid post-construction by using Parts Factories”.

like image 32
Cheers and hth. - Alf Avatar answered Oct 10 '22 16:10

Cheers and hth. - Alf