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