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::v开发者_运维百科ector<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.
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.
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.
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:
- Scott Meyers, "Never Call Virtual Functions during Construction or Destruction"
- Steve Dewhurst, "OBJ30-CPP. A class should not invoke its own virtual functions in its constructors or destructors"
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.
精彩评论