views:

122

answers:

4
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>

class Helper 
{
public:
    Helper() { init(); }
    virtual void print() {
        int nSize = m_vItems.size();
        std::cout << "Size : " << nSize << std::endl;
        std::cout << "Items: " << std::endl;
        for(int i=0; i<nSize; i++) {
            std::cout << m_vItems[i] << std::endl;
        }
    }
protected:
    virtual void init() { m_vItems.push_back("A"); }
    std::vector<std::string> m_vItems;
};

class ItemsHelper : public Helper
{
public:
    ItemsHelper() { }
protected:
    virtual void init() { 
        Helper::init();
        m_vItems.push_back("B");
    }
};

int _tmain(int argc, _TCHAR* argv[]) {
    ItemsHelper h;
    h.print();
}

This output's that the size of the vector is 1. I expected the size to be 2 because in the ItemsHelper::init function I called the base class Helper::init() function, then I add a second item to the vector. The problem is, the ItemsHelper::init doesn't get called, the base class init function gets called instead.

I want the ItemsHelper::init function to get called, and I can do that by calling the init function in the ItemsHelper ctor rather than in the base class. BUT, the question is, is there a better way to achieve that and still keep the call to the init() in the base class? Because what if I want to create a Helper object instead of a ItemsHelper, then the init function would never get called.

btw, this is a simplified version of a issue I'm seeing in a much larger object, I just made these objects up for example.

+7  A: 

In a base class constructor, the derived class has not yet been constructed so the overriden function on the derived class is not yet available. There's a FAQ entry on this somewhere... which I can't find.

The simplest solution is to just put the .push_back("A") part of init into the Helper constructor and the .push_back("B") into the ItemsHelper constructor. This seems to do what you are trying to do and cuts out the unnecessary init virtual function.

Charles Bailey
Yeap, I agree that is the best solution!! Keep it simple, I like that! Thank you!
cchampion
I think the C++ FAQ entry you were looking for is: http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.5
jamesdlin
+4  A: 

Mind that virtual functions do not work as "expected" in constructors!

Helper() { init(); }

Here, init() will always call the "init" of the current class (Helper), even though it is marked virtual.

EDIT: There's a similar question on SO.

AndiDog
+2  A: 

The problem is that virtual functions don't work the way you think they do in constructors. When an ItemsHelper is constructed, first the base class Helper in constructed. In its constructor, the type of the object is Helper, so the call to init calls Helper::init(). Then the ItemsHelper constructor is called. There is no way to call a derived class function from a base class constructor. The best you can do is call init() after the ItemsHelper object is constructed.

KeithB
Hey I learn something! It makes since why it's working this way now, thanks!
cchampion
+3  A: 

In general (unless you completely understand how constructors and virtual functions are specified to work), you shouldn't call virtual functions in constructors, since you generally won't get the 'most virtual' version of the function. The quick version of how virtual functions work in constructors is that when the virtual function is called, you'll get the one for the 'current' level of the class heirarchy that is currently being constructed.

See the following articles for details:

Michael Burr