Nick
Nick

Reputation: 10499

How to properly cast my derived class?

I have a base class (car) and a class that inherit the base class (honda):

class car
{
    virtual void polymorphic_class()
    {   }
};

class honda : public car
{   };


When I use the following code and I cast my class a get a null pointer:

list<car> cars;
honda h;
cars.push_back(h);
honda* h_ptr = dynamic_cast<honda*>(&cars.back());
// h_ptr is NULL

Why? How I have to cast properly my object?

Upvotes: 1

Views: 144

Answers (3)

Mike Seymour
Mike Seymour

Reputation: 254431

Polymorphism works on pointers and references, not on object instances.

In this case, your list contains objects of type car, not of any derived type. When you insert a honda, it will copy the car part and ignore the rest; this is sometimes referred to as slicing.

For polymorphism, you could use a list of pointers:

list<car*> cars {new honda};
honda * h_ptr = dynamic_cast<honda*>(cars.back()); // should be a valid pointer

NOTE: If you do allocate using new as in my example, remember to either delete them, or store smart pointers (like std::unique_ptr<car>) rather than raw pointers. You'll also need a virtual destructor in order to delete objects using a base-class pointer.

You can avoid the slicing problem by making the base class abstract; if it contains pure virtual functions, then you can't instantiate objects of that type, only of derived types that override those functions:

class car
{
    virtual ~car() {}
    virtual void do_something() = 0;
};

class honda : public car
{
    void do_something() {}
};

If you don't actually want an abstract interface (e.g. if you only access derived-class functionality using dynamic_cast rather than through virtual functions), then you could make the destructor pure virtual instead; then the derived classes won't have to explicitly override anything. The base class destructor must still be implemented and, due to a quirk of the language, that implementation must be outside the class definition:

class car
{
    virtual ~car() = 0;
};
inline car::~car() {}

class honda : public car {};

This is a somewhat unusual approach, since polymorphism through virtual functions is usually more efficient and more convenient.

Upvotes: 2

gibraltar
gibraltar

Reputation: 1708

Try this

list<car*> cars;
honda *h = new honda();
cars.push_back(h);
honda* h_ptr = dynamic_cast<honda*>(cars.back());

This works.

Upvotes: 0

Luchian Grigore
Luchian Grigore

Reputation: 258548

The reason this doesn't work is object slicing. The objects in cars are no longer hondas, but just cars.

You need a vector of pointers or smart pointers:

list<car*> cars;
honda h;
cars.push_back(&h);
honda* h_ptr = dynamic_cast<honda*>(cars.back());

I'd actually change the design and make car abstract (pure virtual destructor or something). That way you'll also get a compilation error.

Upvotes: 4

Related Questions