所以我几乎了解它是如何工作的,但我无法理解是什么使它有用。您仍然必须定义所有单独的函数,您仍然必须创建每个对象的实例,所以为什么不从该对象调用该函数与创建对象,创建指向父对象的指针并传递派生对象引用,只是为了调用一个函数?我不明白采取这一额外措施的好处。
为什么这样做:
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;
}
他们做的完全一样吗?据我所知,只有一个人使用更多的记忆和更多的混乱。
答案 0 :(得分:16)
您的示例都以相同的方式执行相同的操作
第一个示例使用 Static binding 调用function()
,而第二个示例使用动态绑定调用它。
在第一种情况下,编译器精确地知道在编译时自己调用哪个函数,而在第二种情况下,应该在运行时决定调用哪个函数,这取决于Base指向的对象的类型。类指针。
有什么好处?
优点是更通用和松散耦合的代码。
想象一下类层次结构如下:
使用这些类的调用代码如下:
Shape *basep[] = { &line_obj, &tri_obj,
&rect_obj, &cir_obj};
for (i = 0; i < NO_PICTURES; i++)
basep[i] -> Draw ();
其中,line_obj
,tri_obj
等是具体Shape类Line
,Triangle
等的对象,并且它们存储在指针的数组中。更通用的基类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:学校)并且他们可以学习,他们就可以接受教育。