Reputation: 748
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.
Upvotes: 3
Views: 2454
Reputation: 503
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 e.g.
Circle *circle=new Circle(4,5);
double r=circle->get_radius();
... //do something with r
Upvotes: 2
Reputation: 58
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();
Upvotes: 1
Reputation: 56577
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.
Upvotes: 2
Reputation: 1609
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
Upvotes: 1