cchampion
cchampion

Reputation: 7931

virtual function question

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

Upvotes: 0

Views: 386

Answers (4)

Michael Burr
Michael Burr

Reputation: 340406

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:

Upvotes: 3

CB Bailey
CB Bailey

Reputation: 792847

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.

Upvotes: 7

KeithB
KeithB

Reputation: 17047

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.

Upvotes: 2

AndiDog
AndiDog

Reputation: 70218

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.

Upvotes: 4

Related Questions