Java – Virtual functions in constructors, why do languages differ

cjavalanguage-agnosticnet

In C++ when a virtual function is called from within a constructor it doesn't behave like a virtual function.

I think everyone who encountered this behavior for the first time was surprised but on second thought it made sense:

As long as the derived constructor has not been executed the object is not yet a derived instance.

So how can a derived function be called? The preconditions haven't had the chance to be set up. Example:

class base {
public:
    base()
    {
        std::cout << "foo is " << foo() << std::endl;
    }
    virtual int foo() { return 42; }
};

class derived : public base {
    int* ptr_;
public:
    derived(int i) : ptr_(new int(i*i)) { }
    // The following cannot be called before derived::derived due to how C++ behaves, 
    // if it was possible... Kaboom!
    virtual int foo()   { return *ptr_; } 
};

It's exactly the same for Java and .NET yet they chose to go the other way, and is possibly the only reason for the principle of least surprise?

Which do you think is the correct choice?

Best Answer

There's a fundamental difference in how the languages define an object's life time. In Java and .Net the object members are zero/null initialized before any constructor is run and is at this point that the object life time begins. So when you enter the constructor you've already got an initialized object.

In C++ the object life time only begins when the constructor finishes (although member variables and base classes are fully constructed before it starts). This explains the behaviour when virtual functions are called and also why the destructor isn't run if there's an exception in the constructor's body.

The problem with the Java/.Net definition of object lifetime is that it's harder to make sure the object always meets its invariant without having to put in special cases for when the object is initialized but the constructor hasn't run. The problem with the C++ definition is that you have this odd period where the object is in limbo and not fully constructed.