【问题标题】:Polymorphism with different methods in derived classes派生类中具有不同方法的多态性
【发布时间】:2017-10-12 21:35:08
【问题描述】:

在尝试学习多态性时,我对一件事感到困惑。考虑以下情况:

存在基类Shape 和派生类CircleSquare。通过多态性,我可以在基类中将方法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;
}

但是如果我需要在其中一个派生类中实现一个单独的方法,例如get_radius。如果此方法未在基​​类中作为虚函数实现,我会从编译器收到错误消息。但是如果我这样做,我还必须在派生类Square 中实现方法get_radius,这没有任何意义,因为正方形没有半径。

有没有更好的方法来解决这个问题?谢谢。

【问题讨论】:

  • 您绝对可以只将get_radius 方法添加到Circle。您能否分享有问题的代码和您遇到的错误?
  • 错误 C2039: 'get_radius': 不是 'Shape' 的成员
  • 没有那个错误的代码那是没用的。此外,即使我能猜到它的意思,它在英语中会更有用。
  • 可以在你的派生类中有不同的非虚函数,但是你不能从基类指针调用它们。
  • 如果你有Shape,你不知道它是CircleTriangle还是Square。为什么你认为你可以打电话给get_radius呢?如果你知道你有一个Circle,你绝对可以调用get_radius,你只需要使用正确的类型:Circle* circle = ...

标签: c++ class oop polymorphism


【解决方案1】:

一般来说,如果没有强制转换,您将无法做您想做的事,这是因为运行时多态性的确切含义是:通过通用接口控制类层次结构。你潜在的Circle::get_radius() 不是界面的一部分。

如果您知道您的对象是 Circle,一个可行的解决方案:使用向下转换

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

其中get_radius() 是仅由Circle 实现的成员函数。

注意:一般来说,绝对需要强制转换意味着糟糕的设计,因此最好花一些时间考虑如何更好地设计代码。

【讨论】:

    【解决方案2】:

    您绝对可以将非虚拟成员函数添加到您的派生类中,而无需在基类中定义它们,但是您不能从基类指针访问它们。 即:

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

    您还可以将形状指针投射到您知道您的形状是圆形的情况下,如 vsoftco 在他的回答中提到的那样

    【讨论】:

      【解决方案3】:

      我可能会误解你,但听起来你正试图在main 中调用circle-&gt;get_radius() 来测试它。问题是circle 实际上不是Circle* 对象,而是Shape*。派生类的工作方式,您可以将它们分配给类型为它们扩展/实现的任何类或接口的变量 - 正如您在示例中所做的那样 - 因为派生类包含这些基类/接口的每个成员的定义.反之则不然。正如你所说,get_radius() 对每个Shape 都没有意义。您收到错误不是因为 Circle 缺少它,而是因为您试图获取恰好使用 Circle 函数实现的随机 Shape* 的半径。

      换句话说,您(作为人类)足够聪明,可以查看代码并发现circleCircle*。编译器足够聪明,可以记住“在查找circle 的函数时,使用Circle 定义的函数”,但除此之外,它不知道特定的Shape* 实例与square 有何不同。据它所知,它唯一能对其中任何一个做的事情就是你告诉它每个Shape 可以做的事情。

      这在您不关心特定Shape 对象是什么的情况下很方便——您所需要的只是可以为您提供一个区域的东西。如果您需要知道半径、宽度和高度,或者Shape 拥有而其他人没有的任何其他属性,则需要使用具有更具体类型的变量。

      看看这是否更适合你:

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

      【讨论】:

        【解决方案4】:

        如果你写

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

        然后你尝试使用这个引用来调用get_radius() 方法

        circle->get_radius(); 
        

        如果您没有在 Shape 类中声明虚拟或具体的 get_radius 方法,则会发生错误。出现该错误是因为circle不是对Circle的引用,而是对Shape的引用,所以仅仅因为Shape中没有定义get_radius方法就无法解析方法的定义。
        另一方面,如果你在 Shape 类中声明了 get_radius,你可以(如果它有一个具体的实现)为所有类型的 Shapes 调用 get_radius,它没有多大意义。
        所以正确的方法是使用对 Circle 的引用,例如

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-05-14
          • 2021-02-07
          • 2014-07-31
          • 1970-01-01
          • 1970-01-01
          • 2013-11-19
          • 2014-07-25
          相关资源
          最近更新 更多