简体   繁体   English

派生类中使用不同方法的多态

[英]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 . 存在一个基类Shape和派生类CircleSquare 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 . 通过多态性,我可以在基类get_area方法get_area为虚拟函数,并在派生类CircleSquare实现此功能的各个版本。

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 . 但是,如果我需要在派生类之一中实现单个方法,例如get_radius ,该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. 但是,如果这样做,我还必须在派生类Square实现方法get_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 然后尝试使用此引用来调用get_radius()方法

circle->get_radius(); 

an error occurs, if you didn't declare a get_radius method, virtual or concrete, in the Shape class. 如果未在Shape类中声明虚拟或具体的get_radius方法,则会发生错误。 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. 发生错误的原因是, circle不是对Circle的引用,而是对Shape的引用,因此无法简单地解决此方法的定义,因为Shape中没有定义get_radius方法。
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. 另一方面,如果您在Shape类中声明了get_radius,则可以( 如果有具体的实现 )针对所有Shape调用get_radius,这毫无意义。
So the right way is use a reference to a Circle eg 所以正确的方法是使用对Circle的引用,例如

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. 您可能的Circle::get_radius()不是接口的一部分。

One solution which works in case you know your object is a Circle : use a downcast 如果您知道自己的物体是Circle一种可行的解决方案:使用向下投射

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 . 其中get_radius()是仅由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 如vsoftco在回答中提到的那样,您还可以将形状指针投射到您知道形状为圆形的情况下

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. 我可能会误解您,但听起来您正在尝试在main中调用circle->get_radius()进行测试。 The problem is that circle isn't actually a Circle* object, it's a Shape* . 问题在于, circle实际上不是Circle*对象,而是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 . 如您所说, get_radius()并非对每个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. 您得到错误的原因不是因为Circle缺少它,而是因为您试图获取恰好使用Circle函数实现的随机Shape*的半径。

To put it another way, you (as a human) are smart enough to look at the code and see that circle is a Circle* . 换句话说,您(作为人类)足够聪明,可以查看代码并看到circleCircle* 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 . 编译器足够聪明,可以记住“在查找circle函数时,使用Circle定义的函数”,但是除此之外,它不知道特定的Shape*实例与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. 据它所知,它只能对任何一个 Shape进行处理。

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. 在您不关心对象的Shape是什么的情况下,这很方便-您所需要的只是可以为您提供面积的东西。 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. 如果您需要知道半径,宽度和高度或一个Shape拥有但其他属性没有的其他任何属性,则需要使用具有更特定类型的变量。

See if this works any better for you: 看看这对您是否更好:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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