多态可以使我们以相同的方式处理不同类型的对象,我们可以使用一段代码处理不同类型的对象,只要他们继承/实现了相同的类型。这样我们没有必要为每一种类型的对象编写相同的逻辑,极大的提高了代码的重用。其实,用简单的一句话来形容就是:多态性,允许将子类类型的指针赋值给父类类型的指针,多态性在C++中是通过虚函数实现的。虚函数就是允许被其子类重新定义的成员函数。而子类重新定义父类虚函数的做法,称为“覆盖”,或者称为“重写”。子类重写父类中虚函数时,即使不用virtual声明,该重载函数也是虚函数。
C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数
1:用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数。
2:存在虚函数的类都有一个一维的虚函数表叫做虚表,类的对象有一个指向虚表开始的虚指针。虚表是和类对应的,虚表指针是和对象对应的。
3:多态性是一个接口多种实现,是面向对象的核心,分为类的多态性和函数的多态性。
4:多态用虚函数来实现,结合动态绑定.
5:纯虚函数是虚函数再加上 = 0;
6:抽象类是指包括至少一个纯虚函数的类。
多态的实现主要依赖于下面的三个东西:
虚函数:成员函数加了virtual修饰
虚函数表指针:一个类型有虚函数,则对这个类型提供一个指针,这个指针放在生成对象的前四个字节。同类型的对象共享一张虚函数表。并且不同类型的虚函数表地址不同。
虚函数表:虚函数表中的每个元素都是虚函数的地址。
一个类型一旦出现虚函数,则会生成一张虚函数表,虚函数表中存放的就是虚函数的函数地址,通过这个函数地址可以到代码区中去执行对应的函数。虚函数表中只存放类型中的虚函数,不是虚函数的一概不管。在每个生成的类型对象的前四个字节中存放的是虚函数表指针,通过这个指针可以访问到虚函数表,从而访问其中的函数。同种类型共享虚函数表,不同类型有自己独立的虚函数表,继承关系中的子类和父类属于不同类型,所以有自己独立的函数表。
总结(基类有虚函数的):
1:每一个类都有虚表
2:虚表可以继承,如果子类没有重写虚函数,那么子类虚表中仍然会有该函数的地址,只不过这个地址指向的是基类的虚函数实现,如果基类有3个虚函数,那么基类的虚表中就有三项(虚函数地址),派生类也会虚表,至少有三项,如果重写了相应的虚函数,那么虚表中的地址就会改变,指向自身的虚函数实现,如果派生类有自己的虚函数,那么虚表中就会添加该项。
3:派生类的虚表中虚地址的排列顺序和基类的虚表中虚函数地址排列顺序相同。
这就是c++中的多态性,当c++编译器在编译的时候,发现Father类的Say()函数是虚函数,这个时候c++就会采用晚绑定技术,也就是编译时并不确定具体调用的函数,而是在运行时,依据对象的类型来确认调用的是哪一个函数,这种能力就叫做c++的多态性,我们没有在Say()函数前加virtual关键字时,c++编译器就确定了哪个函数被调用,这叫做早期绑定。
c++的多态性就是通过晚绑定技术来实现的。
c++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数,如果对象类型是派生类,就调用派生类的函数,如果对象类型是基类,就调用基类的函数。
虚函数是在基类中定义的,目的是不确定它的派生类的具体行为,例如:
定义一个基类:class Animal //动物,它的函数为breathe()
再定义一个类class Fish //鱼。它的函数也为breathe()
再定义一个类class Sheep //羊,它的函数也为breathe()
将Fish,Sheep定义成Animal的派生类,然而Fish与Sheep的breathe不一样,一个是在水中通过水来呼吸,一个是直接呼吸,所以基类不能确定该如何定义breathe,所以在基类中只定义了一个virtual breathe,它是一个空的虚函数,具体的函数在子类中分别定义,程序一般运行时,找到类,如果它有基类,再找到它的基类,最后运行的是基类中的函数,这时,它在基类中找到的是virtual标识的函数,它就会再回到子类中找同名函数,派生类也叫子类,基类也叫父类,这就是虚函数的产生,和类的多态性的体现。
这里的多态性是指类的多态性。
函数的多态性是指一个函数被定义成多个不同参数的函数。当你调用这个函数时,就会调用不同的同名函数。
一般情况下(不涉及虚函数),当我们用一个指针/引用调用一个函数的时候,被调用的函数是取决于这个指针/引用的类型。
当设计到多态性的时候,采用了虚函数和动态绑定,此时的调用就不会在编译时候确定而是在运行时确定。不在单独考虑指针/引用的类型而是看指针/引用的对象的类型来判断函数的调用,根据对象中虚指针指向的虚表中的函数的地址来确定调用哪个函数