rkeeler78
rkeeler78

Reputation: 117

C++ Downcasting to Derived Class based off Variable

Suppose I have a base class "Shape", and derived classes "Triangle", "Square", and "Circle". A member of "Shape" is an int "shapeType".

If shapeType==1, then it's a triangle. If shapeType==2, then it's a square. If shapeType==3, then it's a circle.

I'm interested in knowing given I have just a "Shape" object that was once a derived-object, if there is a way to "dynamically" down-cast to the proper derived class by using the shapeType value.

I know I can do a hard-code switch statement, roughly like:

Triangle* t;
Square* s;
Circle* c;

switch (shape->shapeType) {
case 1:
   t = (Triangle*)shape;
case 2: 
   ...
}

However, the above requires me to make a pointer of EVERY derived class possibility. I am wondering if there is a way to do this without hard-coding every class, but instead somehow determine the class type map where the key is the shapeType and the value is the class type.

Upvotes: 6

Views: 12788

Answers (4)

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361662

If they've virtual functions, then use dynamic_cast:

t = dynamic_cast<Triangle*>(shape);
if ( t )
{
     //use t
}

But take a note: you should try defining the classes and virtual functions in such a way that you would hardly need to use dynamic_cast. Prefer well-defined interface, and polymorphism, in general.

Here is one example,

class Shape
{
   public:
     virtual ~Shape() {} //destructor must be virtual - important!
     virtual double Area() const = 0;
};

class Triangle : public Shape
{
   public:
     Triangle(double a, double b, double c);
     virtual double Area() const 
     {
         //calculate area and return it!
     }
};

Shape *s = new Triangle(10, 20, 30);
double aread = s->Area(); //calls Triangle::Area()

No need to use shapeType variable.

Upvotes: 10

paercebal
paercebal

Reputation: 83374

The dynamic_cast is the answer to your problem.

Description

It is used to downcast from a base class into a derived class, all the while making it sure the cast fails should the derived class not be what you think. For example :

void foo(Shape * p_shape)
{
   Triangle * t = dynamic_cast<Triangle *>(p_shape) ;

   // if p_shape is a triangle, or derives from triangle,
   // then t is non-NULL, and you can use it
}

The point is that t will be non-NULL even if p_shape is not exactly a Triangle, but still inherits for triangle. For example, in the case :

Shape
 |
 +-- Square
 |
 +-- Triangle
      |
      +-- EquilateralTriangle
      |
      +-- RectangleTriangle

if shape is a Triangle, or an EquilateralTriangle, or a RectangleTriangle, then t will not be NULL, which is a lot more powerful than your initial solution of marking the exact type using a constant number.

Please note that for the dynamic_cast to work on a class, this class should have at least a virtual method (which is usually the case in the tree-inheritance hierarchy the dynamic_cast is used on)

Throwing dynamic_cast

Instead of using pointers, you could use references, but with references, as the dynamic_cast has no way to return a "failed reference", it will throw a std::bad_cast, which you can catch if necessary:

void foo(Shape & p_shape)
{
   Triangle & t = dynamic_cast<Triangle &>(p_shape) ;

   // if p_shape is a triangle, or derives from triangle,
   // then the dynamic_cast succeeds.
   // If not, a std::bad_cast is thrown
}

dynamic_cast abuse?

To be noted, the pointer-based non-throwing dynamic cast can lead to switch-like code (but if you can't rely on virtual methods, then you'll have to "switch on types"...):

void foo(Shape * p_shape)
{
   if(Triangle * t = dynamic_cast<Triangle *>(p_shape))
   {
      // if p_shape is a triangle, then t is non-NULL,
      // and you can use it
   }
   else if(Square * s = dynamic_cast<Square *>(p_shape))
   {
      // if p_shape is a square, then t is non-NULL
      // and you can use it
   }
   // etc...

Like all "switch on types" code, this is error prone (what if you forget to handle a type ?), but sometimes can't avoided, so it was worth mentioning.

(As a curiosity bonus, IIRC, the if(type * p = ...) notation was at first added to C++ to handle this case and make the code less verbose... Unless I missed something, this notation is not authorized in C#)

RTTI

All in all, the dynamic_cast relies on RTTI (RunTime Type Information), which can sometimes be disabled (at work, until a few years ago, it was decided by "technical experts" that it was unnecessary and thus mandatory to be disabled in our builds... Aaah, the "C-with classes experts"...)

Don't let yourself get caught in a C vs. C++ war: Unless you are working in very constrained environment (i.e. embedded development), RTTI (as all other C++ features like exception handling) should be activated.

More info on RTTI : http://www.cplusplus.com/reference/std/typeinfo/

And perhaps my Stack Overflow question on RTTI will interest you : C++ RTTI Viable Examples

Upvotes: 4

MGZero
MGZero

Reputation: 5973

Use a dynamic cast. http://en.wikipedia.org/wiki/Dynamic_cast

Upvotes: 0

orlp
orlp

Reputation: 117771

You are doing it wrong. If you have to downcast like that you most likely have a very serious design flaw. Virtual member functions should be the solution.

If you really must downcast like this use dynamic_cast.

Upvotes: 2

Related Questions