views:

217

answers:

5

Hi,

Sorry if this is a dupe, I cant find an answer quite right.

I want to call a function from a base class member, and have it resolve to the subclass version. I thought declaring it virtual would do it, but it isn't. Here's my approach:

class GUIWindow
{
public:
    GUIWindow()
    {
        SetupCallbacks();
    }

    virtual void SetupCallbacks()
    {
         // This function always called
    }
};

class GUIListbox : public GUIWindow
{
public:
    void SetupCallbacks()
    {
        // This never called
    }
};

GUIListbox lb; // GUIWindow::SetupCallbacks is called :(

What am I doing wrong?

Many thanks

Si

+7  A: 

Never call a virtual function in the constructor.

AraK
+1. @sipickles, you're calling the virtual method from the base class' constructor--that's before the derived portion of the object is even initialized. Even if it did call through to the derived type's method what would you expect the behavior to be with uninitialized parts? :)
Curt Nichols
If that's what the book actually says, then the place for that book is the garbage can. What the book actually seems to suggest is that "you are too stupid to learn how it works, so never do it". In reality, the proper advice should be "Learn, how virtual functions work during construction and how to use them properly". Virtual function *do work* during contruction, but with some specifics.
AndreyT
Ack, Thanks. This is obvious now!Time to re-read my textbooks!
sipickles
A: 

The quick answer is that you may have to declare a constructor in GUIListbox which calls SetupCallbacks. The reason is more complicated: because GUIListbox doesn't declare a constructor, when you declare an object of that type the GUIWindow constructor is called. When the GUIWindow constructor is running, the object isn't a GUIListbox yet. From the compiler's perspective, it only becomes a GUIListbox once the (empty) constructor for that class starts. So when the first constructor runs, the methods from GUIWindow are called. IOWs, this doesn't work, and will never work in C++ the way that you want it to.

Here's a reference describing this pitfall in detail: http://www.artima.com/cppsource/nevercall.html.

And here's an explanation from my favorite C++ resource: http://yosefk.com/c++fqa/inheritance-mother.html#fqa-23.5

JSBangs
A: 

Your derived type is not constructed yet.

Class Cat : Animal
{
//...
};

When you create a create a Cat object the following happens:

  1. Construct Animal
  2. Construct Cat

When your object goes out of scope or gets destroyed via delete if on the heap, the following happens:

  1. Destruct Cat
  2. Destruct Animal

For this reason you should not call virtual functions in your constructor or destructor. You would even have a pure virtual function call if you did not have an implementation in your base class.

You are going to have to:

GUIListbox lb;
lb.SetupCallbacks();

The point of virtual is so that you can do things like:

GuiWindow *lb = new GuiListbox();
lb->SetupCallback();//Gets correctly resolved to GuiListBox's version
Brian R. Bondy
A: 

The problem is that you are trying to call the virtual function form the constructor. The base class constructor is called before the constructor of the derived class, so the "derived parts" of the object are not created yet when the base classes constructor runs.

The object will behave like an object of the base class type until the base classes constructor is finished and the derived classes constructor starts. This means that calls to virtual functions will not call implementations defined in the derived class, they will just execute the version from the base class.

Also see the C++ FAQ lite for more details and possible workarounds.

sth
+3  A: 

When you call virtual functions during construction of some class C (i.e. while the constructor of C is active), the virtual mechanism works, but it works in restricted mode. The resolution of the virtual calls in the class hierarchy is limited by the class that is currently being constructed (C). This means that the virtual calls will resolve as if the class C is the "final" class in the hierarchy, as if it has no descendants.

The same is true for destructors.

In your example, you are calling a virtual function from constructor of class GUIWindow. As long as the constructor of GUIWindow is active, its virtual mechanism will work as if there are no other classes derived from GUIWindow. This mechanism will completely ignore the existence of GUIListbox class. This is why the resolution of virtual call "stops" at GUIWindow and calls GUIWindow::SetupCallbacks, as if GUIListbox::SetupCallbacks doesn't exist.

You can read about in in C++ FAQ http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.5

AndreyT