简体   繁体   中英

Polymorphism with different methods in derived classes

While trying to learn polymorphism I am confused about one thing. Consider the following situation:

There exists a base class Shape and the derived classes Circle and Square . With polymorphism I can implement the method get_area as virtual function in the base class and implement individual versions of this funtion in the derives classes Circle and Square .

class Shape
{
  public:
    Shape(){}
    virtual int get_area() = 0;
};

class Square: public Shape
{
  public:
    Square(int width, int height)
    {
       // stuff
    }
    int get_area()
    { 
       return width*height; 
    }
};

class Circle: public Shape
{
  public:
    Circle(int radius)
    {
       // stuff
    }
    int get_area()
    { 
       return pi*radius*radius; 
    }
};

int main () 
{
  Shape *circle= new Circle(3);
  Shape *square= new Square(4,5);

  return 0;
}

But what if I need to implement a individual method in one of the derived classes, for example get_radius . I get error-messsages from the compiler, if this method is not implmeneted as virtual function in the base class. But If I do this, I also have to implement the method get_radius in the derived class Square and this does not make any sense, because a square does not have a radius.

Is there a better way to cope with this problem? Thanks.

If you write

Shape *circle=new Circle(4,5);

and then you try to use this reference to call the get_radius() method

circle->get_radius(); 

an error occurs, if you didn't declare a get_radius method, virtual or concrete, in the Shape class. The error occurs because circle is not a reference to a Circle, but a reference to a Shape, so the definition of the method cannot be resolved simply because there's no get_radius method defined in Shape.
On the the other hand, if you declared get_radius in the Shape class, you can ( if it has a concrete implementantion ) call get_radius for all kind of Shapes and it has no much sense.
So the right way is use a reference to a Circle eg

Circle *circle=new Circle(4,5);
double r=circle->get_radius();
... //do something with r 

In general you cannot do what you want without a cast, and that's because runtime polymorphism means exactly that: controlling a class hierarchy via a common interface . Your potential Circle::get_radius() is not a part of the interface.

One solution which works in case you know your object is a Circle : use a downcast

Circle* ptrCircle = dynamic_cast<Circle*>(circle);
int radius = 0;
if(ptrCircle) // the cast succeeded
    radius = ptrCircle->get_radius();

where get_radius() is a member function that's only implemented by Circle .

Note: in general the absolute need for a cast signals a bad design, so it's better to spend some time thinking about how to better design your code.

You can definitely add non-virtual member functions to your derived classes without defining them in the base class, however you can't access these from a base class pointer. ie:

//won't work
Shape* shapeP = new Circle();
shapeP ->get_radius(); 
//will work
Circle* circP = new Circle();
circP->get_radius();

You can also cast your shape pointer for cases that you know your shape is a circle as mentioned by vsoftco in his answer

There's a chance that I could be misunderstanding you, but it sounds like you're trying to call circle->get_radius() in main in order to test it. The problem is that circle isn't actually a Circle* object, it's a Shape* . The way derived classes work, you can assign them to variables typed as any of the classes or interfaces they extend/implement – as you've done in your example – because the derived class includes a definition for every member of those base classes/interfaces. The opposite isn't true; as you said, get_radius() doesn't make sense for every Shape . You're getting errors not because it's missing from Circle but because you're trying to get the radius of a random Shape* that just happens to use the Circle function implementations.

To put it another way, you (as a human) are smart enough to look at the code and see that circle is a Circle* . The compiler is smart enough to remember "when looking up the functions for circle , use those defined by Circle ", but beyond that it has no idea how that particular Shape* instance is different from square . As far as it knows, the only things it can do to either of them are what you've told it every Shape can do.

That's handy in cases where you don't care about what particular Shape an object is – all you need is something that can give you an area. In cases where you need to know the radius, or the width and height, or any other property that one Shape has but others don't, you need to use a variable with the more specific type.

See if this works any better for you:

Circle *circle= new Circle(3);
circle->get_radius();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM