tags:

views:

201

answers:

7

I was reading Google C++ Style Guide, and got confused in the Doing Work in Constructors part. One of the cons of doing heavy work in constructor is:

If the work calls virtual functions, these calls will not get dispatched to the subclass implementations. Future modification to your class can quietly introduce this problem even if your class is not currently subclassed, causing much confusion.

I didn't understand what it means. Could someone provide an explanation and why this may be considered a disadvantage?

+4  A: 

When an object is being constructed, the constructors for base classes are called first. Since the derived class hasn't been initialized yet, any calls to virtual methods during the base class constructor won't have a derived class object to work on.

Cogwheel - Matthew Orlando
A: 

The constructor's job is simply to initialize your object's initial state. If you start performing tasks that rely on actions taken in virtual functions, you can get some unexpected results when subclasses start implementing these functions.

Tim Rupe
A: 

Consider something like this:

class Base
{
public:
    Base() { Init(); }
private:
    void Init() { DooFoo(); }
    virtual void DoFoo() {}
};

class Foo : public Base
{
private:
    void DoFoo() {};
};

int main()
{
    Foo f;
    return 0;
}

When Foo is constructed, the Base constructor is called first, before the Foo constructor is called. Since Foo hasn't been constructed yet, calling a virtual function in the Base constructor will only invoke the DoFoo() implementation in Base, which is very much not what one would expect. (See http://www.artima.com/cppsource/nevercall.html)

In silico
No, it will give a linker error unless `Base::DoFoo` is defined. But if `Base::DoFoo` is defined the program won't crash but will just call `Base::DoFoo` if an instance of `Foo` is constructed. Virtual functions are disabled in constructor and destructor calls.
Philipp
The compiler or linker isn't required to do anything for that snippet. Behavior is undefined because the called function is pure.
Johannes Schaub - litb
OK. At least the GCC linker does emit an error (and the compiler issues a warning). But why is the behavior undefined? Calling a pure virtual function that is defined works fine in other situations.
Philipp
Well, it's the example provided by Scott Meyer (http://www.artima.com/cppsource/nevercall.html). Compilers may or may not detect a call to a virtual function in the constructor at compiler time.
In silico
+1  A: 

If you inherit from the class, the methods that you override/implement will not be called in this case. So, if Employee calls work() in the constructor, then later on you come up with Hourly::work() and SalariedEmployee::work(), those won't get called. Even though they have different implementations, they'll still be treated as Employee, not their special implementations.

MCain
+2  A: 

When an instance of a subclass is created, first is called the constructor of the base class, then the constructor of the subclass.

If the base class constructor calls a virtual method then the method of the base class will be called instead of the subclass, although the instance is that of a subclass. This could be a problem.

A lot more information here: http://www.artima.com/cppsource/nevercall.html

iniju
+9  A: 

I'm blatantly ripping off some example code from the Wikipedia Virtual function page:

#include <iostream>
#include <vector>

class Animal {
    public:
        virtual void eat() const { 
            std::cout << "I eat like a generic Animal." << std::endl; 
        }
        virtual ~Animal() { 
        }
};

class Wolf : public Animal {
    public:
        void eat() const { 
            std::cout << "I eat like a wolf!" << std::endl; 
        }
};

class Fish : public Animal {
    public:
        void eat() const { 
            std::cout << "I eat like a fish!" << std::endl; 
        }
};

If you call eat() inside the Animal constructor, it will call the Animal eat() function every time. Even when you create a Wolf or a Fish object, since the Animal constructor will complete before the subclass object is initialized, the overriding eat functions won't exist yet.

This is a disadvantage because it can cause confusion between what's expected and what actually happens. If I override eat then create an object of my subclass, I expect my overridden function to be called, even from an Animal reference. I expect it because that's what happens when the call is made explicitly by code outside the constructor. The behavior is different inside the constructor, causing me to scratch my head in bewilderment.

Bill the Lizard
No `class Lizard`?
Justin Ardini
@Justin: I cannot *believe* I missed that opportunity. ;)
Bill the Lizard
A: 

The behavior that virtual function calls aren't virtual is well-defined but very surprising, so compilers usually emit warnings.

Philipp