views:

106

answers:

3

I have the following two classes, one inherits from the other

Class A{
  void print(){cout << "A" << endl;}
}

Class B : A{
  void print(){cout << "B" << endl;}
}
Class C : A{
  void print(){cout << "C" << endl;}
}

Then in another class I have the following:

vector<A> things;
if (..)
  things.push_back(C());
else if (..)
 things.push_back(B());

things[0].print();

this always prints A
I'd like it to print B or C depending on which thing I've added to the vector
how do I do this?
I've tried abstraction but I'm not entirely sure how to use it in C++ and it hasn't been working for me

+3  A: 

You need to declare the function virtual in your base class, in this case that's class a:

class A{
  virtual void print(){cout << "A" << endl;}
}

The other classes will pick this up even if you don't write virtual in those (but you can).

Virtual tells the compiler that the function could be overridden. When you don't use virtual, it's called hiding and works differently, as you've found.

SoapBox
when I do that it still doesn't workand if I make them pure virtual functions then I get an error due to the vector instantiating objects of an abstract class
Jean-Bernard Pellerin
The other answer is *partially* correct. It is wrong where it says you "cannot" store A objects in your vector. You can do that, but when you do that, you're causing your vector to ONLY store A objects, even when you try to put B and C in there, the vector actually creates an A object and copies the B or C into it. You need to store pointers to A in your vector to get the behavior you want.
SoapBox
+8  A: 

1) You need to declare print() as virtual in class A.

2) Your vector is incorrect -- you can't store actual class A objects in there; you will need to store pointers to them for the correct behavior (and you will have to clean up after them later) and/or use something like a boost::shared_ptr.

Joe
I was about to say that :) polymorphism is mostly only relevant when using pointers/references ....
tareqHs
+8  A: 

As mentioned, you need virtual functions to enable polymorphic behaviour and can't store classes directly by value in the vector.

When you use a std::vector<A>, you are storing by value and thus objects that you add, e.g. via push_back() are copied to an instance of an A, which means you lose the derived part of the objects. This problem is known as object slicing.

As already suggested you can avoid that by storing pointers (or smart pointers) to the base class, so only pointers are copied into the vector:

std::vector<A*> things;
things.push_back(new B());
// ... use things:
things[0]->print();

// clean up later if you don't use smart pointers:
for(std::vector<A*>::iterator it = things.begin(); it != things.end(); ++it)
    delete *it;
Georg Fritzsche
Keep in mind that if any exceptions occur during the use of the vector, you will have a leak. A solution is of course to use Boost's pointer library. But if that's not possible, a very simple solution might be to make a `pointer_vector<T>` class that has a private `vector<T>` you can access with a `get` function. The destructor of this wrapper does the deletes. Now the objects are guaranteed to be deleted.
GMan