图片 5

C++ 多态的实现及原理

虚函数的概念是在基类中举行的,即把基类中须要定义为虚函数的分子函数注明为virtual。当基类中的有些成员函数被声称为虚函数后,就可以在派生类中被重新定义。在派生类中重新定义时,其函数原型,满含重回类型、函数名、参数个数和品种,参数的逐一都必得与基类中的原型完全一致。

  2:虚表可以持续,假使子类未有重写虚函数,那么子类虚表中照旧会有该函数的地址,只不过这么些地方指向的是基类的虚函数达成,假若基类有3个虚函数,那么基类的虚表中就有三项(虚函数地址卡塔尔,派生类也会虚表,至少有三项,如若重写了相应的虚函数,那么虚表中的地址就能够改进,指向本人的虚函数完成,若是派生类有协和的虚函数,那么虚表中就能增多该项。

当Fish类的fh对象组织完结后,此中间的虚表指针也就被开头化为指向Fish类的虚表。在类型转变后,调用an-breathe,由于an实际指向的是Fish类的靶子,该对象内部的虚表指针指向的是Fish类的虚表,因而最后调用的是Fish类的breathe函数。

将Fish,Sheep定义成Animal的派生类,但是Fish与Sheep的breathe差异等,二个是在水中通过水来呼吸,三个是直接呼吸,所以基类不可能鲜明该如何定义breathe,所以在基类中只定义了三个virtual
breathe,它是一个空的虚函数,具体的函数在子类中分别定义,程序通常运转时,找到类,借使它有基类,再找到它的基类,最后运维的是基类中的函数,那时,它在基类中找到的是virtual标志的函数,它就能再回来子类中找同名函数,派生类也叫子类,基类也叫父类,那正是虚函数的发出,和类的多态性的反映。

 1 #include "stdafx.h"
 2 #include <iostream> 
 3 #include <stdlib.h>
 4 using namespace std; 
 5 
 6 class Father
 7 {
 8 public:
 9     void Face()
10     {
11         cout << "Father's face" << endl;
12     }
13 
14     void Say()
15     {
16         cout << "Father say hello" << endl;
17     }
18 };
19 
20 
21 class Son:public Father
22 {
23 public:     
24     void Say()
25     {
26         cout << "Son say hello" << endl;
27     }
28 };
29 
30 void main()
31 {
32     Son son;
33     Father *pFather=&son; // 隐式类型转换
34     pFather->Say();
35 }

  那正是c++中的多态性,当c++编写翻译器在编写翻译的时候,发掘Father类的Say(State of Qatar函数是虚函数,这时c++就可以动用晚绑定技艺,也等于编写翻译时并不明确具体调用的函数,而是在运营时,依靠对象的连串来确认调用的是哪四个函数,这种力量就称为c++的多态性,大家尚无在Say(卡塔尔函数前加virtual关键字时,c++编写翻译器就鲜明了哪个函数被调用,那叫做前期绑定。

  

 1 #include "stdafx.h"
 2 #include <iostream> 
 3 #include <stdlib.h>
 4 using namespace std; 
 5 
 6 class Father
 7 {
 8 public:
 9     void Face()
10     {
11         cout << "Father's face" << endl;
12     }
13 
14     virtual void Say()
15     {
16         cout << "Father say hello" << endl;
17     }
18 };
19 
20 
21 class Son:public Father
22 {
23 public:     
24     void Say()
25     {
26         cout << "Son say hello" << endl;
27     }
28 };
29 
30 void main()
31 {
32     Son son;
33     Father *pFather=&son; // 隐式类型转换
34     pFather->Say();
35 }

 从内部存款和储蓄器角度看

  正如广大人那么以为,在上面包车型大巴代码中,大家掌握pFather实际上指向的是Son类的指标,我们期望输出的结果是son类的Say方法,那么想到达到这种结果,将要用到虚函数了。

    图片 1

这段日子大家看一个反映c++多态性的例子,看看输出结果:

  从编写翻译的角度来看:

  正是由于种种对象调用的虚函数都是因而虚表指针来索引的,也就决定了虚表指针的科学开头化是可怜主要的,换句话说,在虚表指针未有正确伊始化早先,大家不可以去调用虚函数,那么虚表指针是在哪些时候,或许哪些地方初步化呢?

  前边输出的结果是因为编译器在编译的时候,就曾经规定了目的调用的函数之处,要减轻那么些主题素材将要动用晚绑定,当编译器使用晚绑准期候,就能够在运转时再去明确指标的档期的顺序以至科学的调用函数,而要让编写翻译器接纳晚绑定,就要在基类中声称函数时使用virtual关键字,那样的函数我们就叫做虚函数,一旦某些函数在基类中宣示为virtual,那么在享有的派生类中该函数都以virtual,而无需再显式地宣称为virtual。

这正是说哪些牢固虚表呢?编写翻译器其余还为种种对象提供了叁个虚表指针(即vptrState of Qatar,那一个指针指向了对象所属类的虚表,在程序运转时,依据指标的花色去先导化vptr,进而让vptr正确的针对性了所属类的虚表,进而在调用虚函数的时候,能够找到科学的函数,对于第二段代码程序,由于pFather实际指向的指标类型是Son,因而vptr指向的Son类的vtable,当调用pFather->Son(卡塔尔(قطر‎时,依据虚表中的函数地址找到的正是Son类的Say(卡塔尔函数.

 
3:多态性是多个接口四种完成,是面向对象的主导,分为类的多态性和函数的多态性。
 

  图片 2

  虚函数是在基类中定义的,目标是不鲜明它的派生类的现实性行为,举例:

输出的结果为:
图片 3

  代码稍稍更改一下,看一上周转结果

小编们组织Son类的对象时,首先要调用Father类的构造函数去组织Father类的靶子,然后才调用Son类的结构函数实现自己部分的布局,进而拼接出三个全体的Son类对象。当大家将Son类对象转换为Father类型时,该指标就被感觉是原对象整个内部存款和储蓄器模型的上半有些,也等于上海教室中“Father的指标所占内部存款和储蓄器”,那么当大家应用类型转变后的靶子指针去调用它的办法时,当然也正是调用它所在的内部存储器中的方法,由此,输出“Father
Say hello”,也就马到功成了。

  编写翻译器在编写翻译的时候,开掘Father类中有虚函数,那个时候编写翻译器会为各样包括虚函数的类创造四个虚表(即
vtableState of Qatar,该表是二个一维数组,在这里个数组中贮存各类虚函数的地址,

  再定义八个类class Sheep //羊,它的函数也为breathe(State of Qatar

出口结果:
图片 4

    c++编写翻译器在编写翻译的时候,要鲜明各类对象调用的函数(非虚函数)的地方,这称之为早期绑定,当大家将Son类的对象son之处赋给pFather时,c++编写翻译器进行了类型转变,那时c++编译器以为变量pFather保存的正是Father对象的地点,当在main函数中实行pFather->Say(卡塔尔(قطر‎,调用的当然便是Father对象的Say函数

 

 
2:存在虚函数的类都有三个一维的虚函数表叫做虚表,类的指标有一个照准虚表起首的虚指针。虚表是和类对应的,虚表指针是和对象对应的。
 

 

Son类对象的内存模型如上海体育场面

作者们在main(State of Qatar函数中率先定义了三个Son类的靶子son,接着定义了三个指向Father类的指针变量pFather,然后使用该变量调用pFather->Say(State of Qatar.预计超级多个人反复将这种气象和c++的多态性搞混淆,以为son实际上是Son类的对象,应该是调用Son类的Say,输出”Son
say hello”,然则结果却不是.

  答案是在架构函数中张开虚表的创始和虚表指针的开头化,在构造子类对象时,要先调用父类的布局函数,那时候编写翻译器只“见到了”父类,并不知道后边是还是不是还恐怕有继任者,它最初化父类对象的虚表指针,该虚表指针指向父类的虚表,当推行子类的布局函数时,子类对象的虚表指针被开端化,指向自己的虚表。

C++的多态性用一句话总结就是:在基类的函数前拉长virtual关键字,在派生类中重写该函数,运转时将会依照目的的实在类型来调用相应的函数。假诺目的类型是派生类,就调用派生类的函数;假若目标类型是基类,就调用基类的函数

  函数的多态性是指叁个函数被定义成多个不一致参数的函数。当您调用那么些函数时,就能够调用分裂的同名函数。

我们先看个例证

图片 5

  这里的多态性是指类的多态性。

  5:纯虚函数是虚函数再增进 = 0;  

貌似情状下(不关乎虚函数卡塔尔,当大家用一个指针/引用调用多个函数的时候,被调用的函数是决定于那些指针/援用的类型。

 

大家开掘结果是”Son say
hello”也就是基于指标的档案的次序调用了不利的函数,那么当大家将Say(卡塔尔国注明为virtual时,背后发生了哪些。

  定义叁个基类:class Animal //动物,它的函数为breathe(卡塔尔(قطر‎

纯虚函数:virtual void
fun(State of Qatar=0;即抽象类!必需在子类完结这一个函数,即先有名称,未有内容,在派生类达成内容。

  1:用virtual关键字注脚的函数叫做虚函数,虚函数确定是类的积极分子函数。  

  1:每叁个类都有虚表

  c++的多态性用一句话总结正是:在基类的函数前增进virtual关键字,在派生类中重写该函数,运维时将会依照目的的其实类型来调用相应的函数,若是目的类型是派生类,就调用派生类的函数,假诺指标类型是基类,就调用基类的函数。

  4:多态用虚函数来促成,结合动态绑定.  

  6:抽象类是指包罗起码叁个纯虚函数的类。

  c++的多态性正是通过晚绑定本事来实现的。

 1 #include "stdafx.h"
 2 #include <iostream> 
 3 #include <stdlib.h>
 4 using namespace std; 
 5 
 6 class CA 
 7 { 
 8 public: 
 9     void f() 
10     { 
11         cout << "CA f()" << endl; 
12     } 
13     virtual void ff() 
14     { 
15         cout << "CA ff()" << endl; 
16         f(); 
17     } 
18 }; 
19 
20 class CB : public CA 
21 { 
22 public : 
23     virtual void f() 
24     { 
25         cout << "CB f()" << endl; 
26     } 
27     void ff() 
28     { 
29         cout << "CB ff()" << endl; 
30         f(); 
31         CA::ff(); 
32     } 
33 }; 
34 class CC : public CB 
35 { 
36 public: 
37     virtual void f() 
38     { 
39         cout << "C f()" << endl; 
40     } 
41 }; 
42 
43 int main() 
44 { 
45     CB b; 
46     CA *ap = &b; 
47     CC c; 
48     CB &br = c; 
49     CB *bp = &c; 
50 
51     ap->f(); 
52     cout << endl;
53 
54     b.f(); 
55     cout << endl;
56 
57     br.f(); 
58     cout << endl;
59 
60     bp->f(); 
61     cout << endl;
62 
63     ap->ff(); 
64     cout << endl;
65 
66     bp->ff(); 
67     cout << endl;
68 
69     return 0; 
70 } 

  再定义三个类class Fish //鱼。它的函数也为breathe(卡塔尔国

  

  3:派生类的虚表中虚地址的排列顺序和基类的虚表中虚函数地址排列顺序相通。

 

当设计到多态性的时候,接受了虚函数和动态绑定,那时候的调用就不会在编写翻译时候分明而是在运营时规定。不在单独构思指针/引用的花色而是看指针/援用的指标的花色来判断函数的调用,依据指标中虚指针指向的虚表中的函数的地点来规定调用哪个函数

  总括(基类有虚函数的卡塔尔国:

发表评论

电子邮件地址不会被公开。 必填项已用*标注