c++boostptr-vector

BOOST and C++: can't seem to get polymorphism to work


I'm using ptr_vector to store "shapes". I'm trying to fill it with derived shape classes, such as "circles", and every time I try to downcast them I get bad cast.

class Shape
{
public:
    virtual ~Shape() {};
    virtual void print() { std::cout << "shape" << std::endl; };
};

class Circle :
    public Shape
{
public:
    void print() { std::cout << "circle" << std::endl; };
};

int main()
{
    boost::ptr_vector<Shape> shapes;
    shapes.push_back(new Circle);

    BOOST_FOREACH(Shape shape, shapes)
    {
        Circle& tempCircle = dynamic_cast<Circle&>(shape);
        if(&tempCircle != NULL)
            tempCircle.print();
    }

    system("PAUSE");
}

Solution

  • The problem is that your shape is an object whose type is Shape, and not a reference to an object whose (dynamic) type is Circle.

    Polymorphism only works with references or pointers. When treating objects as values and copy-construct or move-construct objects of a base class from objects of a derived class, what you get is slicing (definitely not something you want).

    Try this instead:

    BOOST_FOREACH(Shape& shape, shapes)
    //                 ^
    

    It would also make sense to use a reference to const, probably - since you are not going to modify the referenced object inside the loop, so:

    BOOST_FOREACH(Shape const& shape, shapes)
    //                  ^^^^^^  
    {
        Circle const& tempCircle = dynamic_cast<Circle const&>(shape);
        //     ^^^^^^                                  ^^^^^^
    
        // ...
    }
    

    Also notice, that C++11 has range-based for loops, which make BOOST_FOREACH kind of obsolete. So if C++11 is an option, you could write:

    for (auto const& shape : shapes)
    {
        Circle const& tempCircle = dynamic_cast<Circle const&>(shape);
        //     ^^^^^^                                  ^^^^^^
    
        // ...
    }
    

    This said, it makes sense to point out (as Chad does in the comments) that you do not need to perform a dynamic downcast here, since print() is a virtual function. When doing:

    shape.print();
    

    The function call will be dispatched to Circle::print() if the object referenced by Shape is an instance of Circle.