有人可以解释多态性的好处吗?

时间:2012-02-05 06:58:40

标签: c++ polymorphism

所以我几乎了解它是如何工作的,但我无法理解是什么使它有用。您仍然必须定义所有单独的函数,您仍然必须创建每个对象的实例,所以为什么不从该对象调用该函数与创建对象,创建指向父对象的指针并传递派生对象引用,只是为了调用一个函数?我不明白采取这一额外措施的好处。

为什么这样做:

class Parent
{
    virtual void function(){};
};

class Derived : public Parent
{
    void function()
    {
    cout << "derived";
    }
};

int main()
{
    Derived foo;
    Parent* bar = &foo;
    bar->function();
    return -3234324;
}

vs this:

class Parent
{
    virtual void function(){};
};

class Derived : public Parent
{
    void function()
    {
    cout << "derived";
    }
};

int main()
{
    Derived foo;
    foo.function();
    return -3234324;
}

他们做的完全一样吗?据我所知,只有一个人使用更多的记忆和更多的混乱。

10 个答案:

答案 0 :(得分:16)

您的示例都以相同的方式执行相同的操作 第一个示例使用 Static binding 调用function(),而第二个示例使用动态绑定调用它。

在第一种情况下,编译器精确地知道在编译时自己调用哪个函数,而在第二种情况下,应该在运行时决定调用哪个函数,这取决于Base指向的对象的类型。类指针。

有什么好处?
优点是更通用和松散耦合的代码。

想象一下类层次结构如下:

enter image description here

使用这些类的调用代码如下:

Shape *basep[] = { &line_obj, &tri_obj,
                   &rect_obj, &cir_obj};
for (i = 0; i < NO_PICTURES; i++)
    basep[i] -> Draw ();

其中,line_objtri_obj等是具体Shape类LineTriangle等的对象,并且它们存储在指针的数组中。更通用的基类Shape的类型。

这提供了额外的灵活性和松散耦合,如果你需要添加另一个具体的形状类说Rhombus,调用代码不需要改变很多,因为它引用所有具有指向Base的指针的具体形状班Shape。您只需要使Base类指针指向新的具体类。

同时调用代码可以调用那些类的适当方法,因为Draw()方法在这些类中是虚拟的,并且调用的方法将在运行时决定,具体取决于基类指针的对象指着。

以上是应用着名 Open Closed Principle SOLID design principles 的一个很好的示例。

答案 1 :(得分:6)

假设你想让某人出现在工作中。你不知道他们是需要开车,坐公共汽车,步行还是什么。你只是希望他们出现在工作中。有了多态性,你只需告诉他们出现工作,他们就是这样。如果没有多态性,你必须弄清楚他们需要如何开始工作并将他们引导到那个过程。

现在说有些人开始使用赛格威工作。如果没有多态性,那些告诉某人上班的每一段代码都必须学习这种新的工作方式,以及如何找出以这种方式工作的人以及如何告诉他们这样做。使用多态性,你可以将代码放在一个地方,在Segway-rider的实现中,所有告诉人们去上班的代码告诉Segway-riders带他们的Segways,尽管它不知道这是什么它正在做。

有许多现实世界的编程类比。假设您需要告诉某人他们需要调查的问题。他们首选的联系机制可能是电子邮件,也可能是即时消息。也许这是一条短信。使用多态通知方法,您可以添加新的通知机制,而无需更改可能需要使用它的每一段代码。

答案 2 :(得分:2)

如果你有一个共享一个共同祖先的对象的列表/数组,并且你对它们做了一些常见的事情,或者你有一个重写的方法,那么

多态是很好的。我从中学习了这个概念的例子,使用形状和覆盖draw方法。他们都做不同的事情,但他们都是'形状',都可以画出来。你的例子并没有真正做任何有用的保证使用多态

答案 3 :(得分:2)

有用的多态性的一个很好的例子是.NET Stream类。它有许多实现,如“FileStream”,“MemoryStream”,“GZipStream”等。使用“Stream”而不是“FileStream”的算法可以在任何其他流类型上重复使用,几乎不需要修改。

答案 4 :(得分:2)

有无数的多态性使用的例子。以一个表示GUI小部件的类为例。大多数基类都有类似的东西:

class BaseWidget
{
...
virtual void draw() = 0;
...
};

这是一个纯虚函数。这意味着继承Base的所有类都需要实现它。当然,GUI中的所有小部件都需要自己绘制,对吧?这就是为什么你需要一个基类,其中包含所有GUI小部件通用的所有函数,这些函数被定义为纯虚拟,因为在任何一个孩子中你都会这样做:

class ChildWidget
{
 ...
  void draw()
  {
     //draw this widget using the knowledge provided by this child class
  }
};


class ChildWidget2
{
 ...
  void draw()
  {
     //draw this widget using the knowledge provided by this child class
  }
};

然后在您的代码中,您无需关心检查您正在绘制的小部件类型。知道如何绘制自己的责任在于小部件(对象)而不是你。所以你可以在你的主循环中做类似的事情:

for(int i = 0; i < numberOfWidgets; i++)
{
    widgetsArray[i].draw();
}

以上将绘制所有小部件,无论它们是ChildWidget1,ChildWidget2,TextBox,Button类型。

希望有助于理解多态性的好处。

答案 5 :(得分:2)

多态中的 poly 表示多个。换句话说,除非存在多个派生函数,否则多态性不相关。

在这个例子中,我有两个派生函数。其中一个是基于mode变量选择的。请注意agnostic_function()不知道选择了哪一个。然而,它调用function()的正确版本。

所以多态性的一点是,你的大多数代码都不需要知道正在使用哪个派生类。要实例化的类的具体选择可以本地化为代码中的单个点。这使代码更清晰,更易于开发和维护。

#include <iostream>
using namespace std;

class Parent
{
public:
    virtual void function() const {};
};

class Derived1 : public Parent
{
    void function() const { cout << "derived1"; }
};

class Derived2 : public Parent
{
    void function() const { cout << "derived2"; }
};

void agnostic_function( Parent const & bar )
{
   bar.function();
}

int main()
{
   int mode = 1;
   agnostic_function
   (
      (mode==1)
      ? static_cast<Parent const &>(Derived1())
      : static_cast<Parent const &>(Derived2())
   );
}

答案 6 :(得分:2)

重用,概括和可扩展性。

我可能有这样的抽象类层次结构:Vehicle&gt;汽车。然后我可以简单地从Car派生出来实现具体类型SaloonCar,CoupeCar等。我在抽象基类中实现了通用代码。我可能还构建了一些与Car结合的其他代码。我的SaloonCar和CoupeCar都是Cars,所以我可以将它们传递给这个客户端代码而无需改动。

现在考虑我可能有一个界面; IInternalCombustionEngine和一个加上这个的课程,比如车库(我知道,我知道,留在我身边)。我可以在单独的类层次结构中定义的类上实现此接口。 E.G。

public abstract class Vehicle {..}

public abstract class Bus : Vehicle, IPassengerVehicle, IHydrogenPowerSource, IElectricMotor {..}

public abstract class Car : Vehicle {..}

public class FordCortina : Car, IInternalCombustionEngine, IPassengerVehicle {..}

public class FormulaOneCar : Car, IInternalCombustionEngine {..}

public abstract class PowerTool {..}

public class ChainSaw : PowerTool, IInternalCombustionEngine {..}

public class DomesticDrill : PowerTool, IElectricMotor {..}

所以,我现在可以说福特科蒂娜的一个对象实例是一辆汽车,它是一辆汽车,它是一辆IInternalCombustionEngine(再次做好了,但是你明白了),它也是一辆乘用车。这是一个强大的结构。

答案 7 :(得分:1)

多态性是OOP的原则之一。使用多态,您可以在运行时选择多个行为。在您的示例中,您有一个Parent实现,如果您有更多实现,您可以在运行时通过参数选择一个。多态有助于解耦应用层。在你的第三部分的样本中使用这个结构,然后它只看到父接口,并且不知道运行时的实现,所以第三方独立于Parent接口的实现。您还可以看到依赖注入模式以获得更好的设计。

答案 8 :(得分:1)

还有一点需要补充。实现运行时插件需要多态性。可以在运行时向程序添加功能。在C ++中,派生类可以实现为共享库。可以对运行时系统进行编程以查看库目录,如果出现新的共享对象,则将其链接并可以开始调用它。这也可以在Python中完成。

答案 9 :(得分:-1)

假设我的School班级有educate()方法。此方法仅接受学习的人员。他们有不同的学习方式。有人抓住,有人只是把它弄干了,等等。

现在我要说学校班上有男生,女生,狗和猫。如果School想要教育他们,我将不得不在School下为不同的对象编写不同的方法。

相反,不同的人物(男孩,女孩,猫......)实现了Ilearnable界面。然后,School课程不必担心教育的内容。

学校只需写一个

public void Educate (ILearnable  anyone)

方法。

我写过猫狗,因为他们可能想去不同类型的学校。只要是某种类型的学校(PetSchool:学校)并且他们可以学习,他们就可以接受教育。

  1. 因此它保存了多个具有相同实现但输入类型不同的方法
  2. 实现与现实场景相匹配,因此很容易设计
  3. 我们可以专注于课程的一部分而忽略其他一切。
  4. 课程的延伸(例如,经过多年的教育,你知道,嘿,学校周围的所有人都必须通过GoGreen计划,每个人都必须以同样的方式种植一棵树。如果你有一个基类所有这些人都是抽象的LivingBeings,我们可以添加一个方法来调用PlantTree并在PlantTree中编写代码。没有人需要在他们继承LivingBeings类的类体中编写代码,只需将它们拼接到PlantTree就可以确保他们可以植入树)。