Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Virtual functions in constructors, why do languages differ?

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?

like image 379
Motti Avatar asked Aug 31 '08 12:08

Motti


People also ask

Can we use virtual function in constructor?

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”.

Where the virtual function should be different?

Where the virtual function should be defined? Explanation: The virtual function should be declared in base class. So that when the derived class inherits from the base class, the functions can be differentiated from the one in base class and another in derived class.

Why should calls to virtual functions be avoided in constructors and destructors?

So, Don't invoke virtual functions from constructors or destructors that attempts to call into the object under construction or destruction, Because the order of construction starts from base to derived and the order of destructors starts from derived to base class.

Why Virtual constructors are not allowed?

In C++, constructor cannot be virtual, because when constructor of a class is executed there is no virtual table in the memory, means no virtual pointer defined yet. So, the constructor should always be non-virtual.


2 Answers

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.

like image 199
Daniel James Avatar answered Oct 20 '22 14:10

Daniel James


Both ways can lead to unexpected results. Your best bet is to not call a virtual function in your constructor at all.

The C++ way I think makes more sense, but leads to expectation problems when someone reviews your code. If you are aware of this situation, you should purposely not put your code in this situation for later debugging's sake.

like image 21
Brian R. Bondy Avatar answered Oct 20 '22 14:10

Brian R. Bondy