开发者

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 开发者_StackOverflow中文版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.


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.


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


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.


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜