I have two classes like this (simplified and renamed):
class Base
{
public:
virtual void operator()( ) { cout << "Base\n";}
};
class Derived : public Base
{
public:
void operator()( ) { cout << "Derived\n"; }
};
void doSomething( Base obj )
{
obj( );
}
int main( )
{
list<Base> bList;
Derived d1, d2, d3;
bList.push_back( d1 );
bList.push_back( d2 );
bList.push_back( d3 );
for( list<Base>::iterator it = bList.begin(); it != bList.end(); it++ )
{
doSomething( *it );
}
return 0;
}
When I run this, I get:
Base
Base
Base
i.e. the Base::operator() is called. Obviously this is not what I want to happen.
I tried changing doSomething to
void doSomething( Base& obj )
but this only works if called directly with a Derived object and not with one from the list. And references can’t be stored in a list.
Is there any way how I can make doSomething “see” that it has to call a derived class’ function (without having to resort to pointers)?
Edit: Problem is pointed out, for other people who might run into this here is a link:
What is object slicing?
The problem isn’t that the compiler doesn’t “see” that it needs to call a function in a derived class, the problem is that you are only storing the base class in your container
list<Base>. This results in what is commonly called object slicing. When the compiler copies yourDerivedobjects into the container that can only holdBaseobjects, it can only copy the base portion of the object and “slices” off the part that makes it aDerivedobject.In order to make use of polymorphism with containers that follow value semantics (ie, assume that you store the actual object in them and not a form of reference to an object), you need store a pointer to said object. I would advise against storing raw pointers in a standard library container as it introduces additional lifecycle management issues – you’d have to control the lifetimes of the pointed-to objects from outside the container, which tends to be a really bad idea and usually either leads to memory leaks or dangling pointers, if not both.
My recommendation would be that if you can use boost, use one of the
ptr_containertemplate classes that were explicitly designed for this purpose. If you can’t and you’re on a modern enough compiler, you can use astd::list<std::shared_ptr<Base> > >and rely on theshared_ptrto manage the objects’ lifecycles.