Whether using dynamic cast for providing input for derived class virtual function is recommended?

164 views Asked by At

I read some of the answers in What is the proper use case for dynamic_cast.

The line which best matched my situation here is

#include<iostream>

class Shape
{
   public:
      virtual void draw()=0;
      virtual ~Shape(){};
};

class  Rectangle : public Shape
{
   public:
      int length;
      int breath;

      void draw()
      {
         std::cout<<"RECTANGE"<<std::endl;
      }

};


class  Circle : public Shape
{
   public:
      int diameter;

      void draw()
      {
         std::cout<<"CIRCLE"<<std::endl;
      }

};


/*Abstract Factory*/

Shape* getShapeObj(int type)
{
   switch(type)
   {
      case 1:
         return new Rectangle;

      case 2:
         return new Circle;

         /*  many types will be added here in future. */
   }

   return NULL;
};

void drawShapes(Shape *p_shape[],int len)
{
   for(int i=0;i<len;i++)
      p_shape[i]->draw();
}

int main()
{
   Shape *l_shape[2];
   l_shape[0]=getShapeObj(1);
   l_shape[1]=getShapeObj(2);
   Rectangle *l_rec=dynamic_cast<Rectangle*>(l_shape[0]);

   if(l_rec)
   {
      l_rec->length=10;
      l_rec->breath=20;
   }

   Circle *l_circle=dynamic_cast<Circle*>(l_shape[1]);

   if(l_circle)
      l_circle->diameter=25;

   drawShapes(l_shape,2);

}

Essentially, virtual functions only work in some cases, not all of them.

My problem is to pass the input for the virtual function and inputs will vary from type to type. Whether using dynamic cast is recommended here?

2

There are 2 answers

2
StoryTeller - Unslander Monica On

The solution is perfect forwarding of function parameters, introduced in c++11.

template<typename ...CtorArgs>
Shape* getShapeObj(int type, CtorArgs&& ctor_args...)
{
   switch(type)
   {
      case 1:
         return new Rectangle(std::forward<CtorArgs>(ctor_args)...);
     // many types will be added here in future.
   }

    return NULL;
}

Obviously making the function a template, defeats the purpose of hiding the hierarchy (as well as forcing rather strict requirements on the number of parameters to the constructors). But if the base contains a map of functions that do the construction, which each derived class updates with a pointer to function that constructs it, you can still have information hiding.


I have recently written an answer about storing type erased function pointers in a map, with some static type checking forwarded to run time.

2
alexeykuzmin0 On

In this particular case, looks like your main function is taking too much responsibility. What if you have Circle, Hexagon, MyFancyFigure types? All of them should be initialized in main in different branches?

It would be much better to move that "initialization" logic to a separate virtual function init in your classes (or even to the constructor). The code would look like this:

class Shape
{
   public:
      virtual void draw()=0;
      virtual void init()=0;
      virtual ~Shape(){};
};

class  Rectangle : public Shape
{
   public:
      int length;
      int breath;

      void draw()
      {
         //Draw Rectangle
      }

      void init()
      {
         length = 10;
         breath = 20;
      }
};

int main()
{
   Shape *l_shape=getShapeObj(1);
   // Calls different code of "init" method depending on the actual object type
   l_shape->init();
   l_shape->draw();
   delete l_shape;
}

Also, please note that this initialization logic may be place in some other place, like constructor of the class or the factory method. But main is definitely the wrong place.