2

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?

Community
  • 1
  • 1
VINOTH ENERGETIC
  • 1,775
  • 4
  • 23
  • 38
  • 1
    *"to pass the input for the virtual function and inputs will vary from type to type"* - I don't see a place in your code where you use a virtual functions with some inputs... Could you elaborate? You cannot override a virtual function and change its parameters... – Holt Dec 19 '16 at 10:23
  • What does the above code achieve that `Rectangle *l_rec = new Rectangle(10, 20);` does not achieve? (Or even better `auto l_rec = std::make_unique(10,20);`). I understand this is a simplified example, but I don't see what the complex example would look like. – Martin Bonner supports Monica Dec 19 '16 at 10:26
  • @Holt updated the code with elaborated example – VINOTH ENERGETIC Dec 19 '16 at 10:38
  • @VinothKumar Thanks. What is the real purpose of this `getShapeObj` function? Why can't you simply do e.g. `l_shape[0] = new Rectangle(10, 2);` (after having created a corresponding constructor)? – Holt Dec 19 '16 at 10:48

2 Answers2

2

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.

Community
  • 1
  • 1
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 2
    This will only works if all subclasses have the same constructor parameters since the whole function body need to well-formed. – Holt Dec 19 '16 at 10:24
  • 1
    @Holt - Yes. Braking it apart to accommodate different c'tor parameters will require a level of indirection, say via tag dispatch. But the point is that there are ways to avoid a `dynamic_cast`. – StoryTeller - Unslander Monica Dec 19 '16 at 10:26
0

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.

alexeykuzmin0
  • 6,344
  • 2
  • 28
  • 51
  • @alexykuzmin My input will vary depend on type. Mean to say you have hardcoded the input in init. – VINOTH ENERGETIC Dec 19 '16 at 10:41
  • @VinothKumar If it depends on type, you probably have now more than one if in `main` function. This code will look much better when distributed between multiple `init` functions of derived classes. I hope I understood you correctly. – alexeykuzmin0 Dec 19 '16 at 10:44