Reputation: 117
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
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
Reputation: 83374
The dynamic_cast
is the answer to your problem.
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)
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#)
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
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