2

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");
}
GEOCHET
  • 21,119
  • 15
  • 74
  • 98
dubesinhower
  • 43
  • 1
  • 3

2 Answers2

6

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.

Community
  • 1
  • 1
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Also note, that since `print()` is a virtual function, the `dynamic_cast` is unnecessary in this case. – Chad Jun 27 '13 at 18:34
  • @Chad: Oh, right, I missed that part. I will edit, thank you :) – Andy Prowl Jun 27 '13 at 18:35
  • @Chad: when i use for(auto const& shape : shapes), when i try to call shape.print(), it says that the object has type qualifiers that are not compatible with the member function :( – dubesinhower Jun 27 '13 at 19:36
  • @dubesinhower: You should make `print()` a `const` member function (so `void print() const { ... }`), since it does not need to alter the state of the object it is invoked on – Andy Prowl Jun 27 '13 at 19:40
0

Also, you're not using dynamic_cast correctly. If you dynamic_cast a reference, and the object does not actually belong to the class you're casting it to, then the cast throws std::bad_cast. It does not return null because there's no such thing as a reference with a null address. So here's the correct way:

Circle* tempCircle = dynamic_cast<Circle*>(&shape);
if(tempCircle != NULL)
    tempCircle->print();

(You can actually have a reference r with &r == NULL, but only after dereferencing a null pointer, which has undefined behavior.)

Derek Ledbetter
  • 4,675
  • 3
  • 20
  • 18