亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

詳細解析C++多態的實現及原理

2020-02-24 14:27:57
字體:
來源:轉載
供稿:網友

基類的函數前面是virtual關鍵字,該函數在派生類中被重寫,在運行時,將根據對象的實際類型調用相應的函數,接下來武林技術頻道小編將詳細解析C++多態的實現及原理,一起跟著小編的步伐來學習吧!

1. 用virtual關鍵字申明的函數叫做虛函數,虛函數肯定是類的成員函數。
2. 存在虛函數的類都有一個一維的虛函數表叫做虛表。類的對象有一個指向虛表開始的虛指針。虛表是和類對應的,虛表指針是和對象對應的。
3. 多態性是一個接口多種實現,是面向對象的核心。分為類的多態性和函數的多態性。
4. 多態用虛函數來實現,結合動態綁定。
5. 純虛函數是虛函數再加上= 0。
6. 抽象類是指包括至少一個純虛函數的類。

純虛函數:virtual void breathe()=0;即抽象類!必須在子類實現這個函數!即先有名稱,沒內容,在派生類實現內容!

我們先看一個例子:

?

#include <iostream.h>
class animal
{
public:
?????? void sleep()
?????? {
????????????? cout<<"animal sleep"<<endl;
?????? }
?????? void breathe()
?????? {
????????????? cout<<"animal breathe"<<endl;
?????? }
};
class fish:public animal
{
public:
?????? void breathe()
?????? {
????????????? cout<<"fish bubble"<<endl;
?????? }
};
void main()
{
?????? fish fh;
?????? animal *pAn=&fh; // 隱式類型轉換
?????? pAn->breathe();
}


注意,在例1-1的程序中沒有定義虛函數??紤]一下例1-1的程序執行的結果是什么?
答案是輸出:animal breathe

?

我們在main()函數中首先定義了一個fish類的對象fh,接著定義了一個指向animal類的指針變量pAn,將fh的地址賦給了指針變量pAn,然后利用該變量調用pAn->breathe()。許多學員往往將這種情況和C++的多態性搞混淆,認為fh實際上是fish類的對象,應該是調用fish類的breathe(),輸出“fish bubble”,然后結果卻不是這樣。下面我們從兩個方面來講述原因。

1、 編譯的角度
C++編譯器在編譯的時候,要確定每個對象調用的函數(要求此函數是非虛函數)的地址,這稱為早期綁定(early binding),當我們將fish類的對象fh的地址賦給pAn時,C++編譯器進行了類型轉換,此時C++編譯器認為變量pAn保存的就是animal對象的地址。當在main()函數中執行pAn->breathe()時,調用的當然就是animal對象的breathe函數。

2、 內存模型的角度
我們給出了fish對象內存模型,如下圖所示:

我們構造fish類的對象時,首先要調用animal類的構造函數去構造animal類的對象,然后才調用fish類的構造函數完成自身部分的構造,從而拼接出一個完整的fish對象。當我們將fish類的對象轉換為animal類型時,該對象就被認為是原對象整個內存模型的上半部分,也就是圖1-1中的“animal的對象所占內存”。那么當我們利用類型轉換后的對象指針去調用它的方法時,當然也就是調用它所在的內存中的方法。因此,輸出animal breathe,也就順理成章了。

正如很多學員所想,在例1-1的程序中,我們知道pAn實際指向的是fish類的對象,我們希望輸出的結果是魚的呼吸方法,即調用fish類的breathe方法。這個時候,就該輪到虛函數登場了。

前面輸出的結果是因為編譯器在編譯的時候,就已經確定了對象調用的函數的地址,要解決這個問題就要使用遲綁定(late binding)技術。當編譯器使用遲綁定時,就會在運行時再去確定對象的類型以及正確的調用函數。而要讓編譯器采用遲綁定,就要在基類中聲明函數時使用virtual關鍵字(注意,這是必須的,很多學員就是因為沒有使用虛函數而寫出很多錯誤的例子),這樣的函數我們稱為虛函數。一旦某個函數在基類中聲明為virtual,那么在所有的派生類中該函數都是virtual,而不需要再顯式地聲明為virtual。
下面修改例1-1的代碼,將animal類中的breathe()函數聲明為virtual,如下:

?

#include <iostream.h>
class animal
{
public:
?void sleep()
?{
??cout<<"animal sleep"<<endl;
?}
?virtual void breathe()
?{
??cout<<"animal breathe"<<endl;
?}
};

?

class fish:public animal
{
public:
?void breathe()
?{
??cout<<"fish bubble"<<endl;
?}
};
void main()
{
?fish fh;
?animal *pAn=&fh; // 隱式類型轉換
?pAn->breathe();
}


大家可以再次運行這個程序,你會發現結果是“fish bubble”,也就是根據對象的類型調用了正確的函數。
那么當我們將breathe()聲明為virtual時,在背后發生了什么呢?

?

編譯器在編譯的時候,發現animal類中有虛函數,此時編譯器會為每個包含虛函數的類創建一個虛表(即vtable),該表是一個一維數組,在這個數組中存放每個虛函數的地址。對于例1-2的程序,animal和fish類都包含了一個虛函數breathe(),因此編譯器會為這兩個類都建立一個虛表,(即使子類里面沒有virtual函數,但是其父類里面有,所以子類中也有了)如下圖所示:


?


?

那么如何定位虛表呢?編譯器另外還為每個類的對象提供了一個虛表指針(即vptr),這個指針指向了對象所屬類的虛表。在程序運行時,根據對象的類型去初始化vptr,從而讓vptr正確的指向所屬類的虛表,從而在調用虛函數時,就能夠找到正確的函數。對于例1-2的程序,由于pAn實際指向的對象類型是fish,因此vptr指向的fish類的vtable,當調用pAn->breathe()時,根據虛表中的函數地址找到的就是fish類的breathe()函數。

正是由于每個對象調用的虛函數都是通過虛表指針來索引的,也就決定了虛表指針的正確初始化是非常重要的。換句話說,在虛表指針沒有正確初始化之前,我們不能夠去調用虛函數。那么虛表指針在什么時候,或者說在什么地方初始化呢?

答案是在構造函數中進行虛表的創建和虛表指針的初始化。還記得構造函數的調用順序嗎,在構造子類對象時,要先調用父類的構造函數,此時編譯器只“看到了”父類,并不知道后面是否后還有繼承者,它初始化父類對象的虛表指針,該虛表指針指向父類的虛表。當執行子類的構造函數時,子類對象的虛表指針被初始化,指向自身的虛表。對于例2-2的程序來說,當fish類的fh對象構造完畢后,其內部的虛表指針也就被初始化為指向fish類的虛表。在類型轉換后,調用pAn->breathe(),由于pAn實際指向的是fish類的對象,該對象內部的虛表指針指向的是fish類的虛表,因此最終調用的是fish類的breathe()函數。

要注意:對于虛函數調用來說,每一個對象內部都有一個虛表指針,該虛表指針被初始化為本類的虛表。所以在程序中,不管你的對象類型如何轉換,但該對象內部的虛表指針是固定的,所以呢,才能實現動態的對象函數調用,這就是C++多態性實現的原理。

總結(基類有虛函數):
1. 每一個類都有虛表。

2. 虛表可以繼承,如果子類沒有重寫虛函數,那么子類虛表中仍然會有該函數的地址,只不過這個地址指向的是基類的虛函數實現。如果基類有3個虛函數,那么基類的虛表中就有三項(虛函數地址),派生類也會有虛表,至少有三項,如果重寫了相應的虛函數,那么虛表中的地址就會改變,指向自身的虛函數實現。如果派生類有自己的虛函數,那么虛表中就會添加該項。

3. 派生類的虛表中虛函數地址的排列順序和基類的虛表中虛函數地址排列順序相同。

這就是C++中的多態性。當C++編譯器在編譯的時候,發現animal類的breathe()函數是虛函數,這個時候C++就會采用遲綁定(late binding)技術。也就是編譯時并不確定具體調用的函數,而是在運行時,依據對象的類型(在程序中,我們傳遞的fish類對象的地址)來確認調用的是哪一個函數,這種能力就叫做C++的多態性。我們沒有在breathe()函數前加virtual關鍵字時,C++編譯器在編譯時就確定了哪個函數被調用,這叫做早期綁定(early binding)。

C++的多態性是通過遲綁定技術來實現的。

C++的多態性用一句話概括就是:在基類的函數前加上virtual關鍵字,在派生類中重寫該函數,運行時將會根據對象的實際類型來調用相應的函數。如果對象類型是派生類,就調用派生類的函數;如果對象類型是基類,就調用基類的函數。

虛函數是在基類中定義的,目的是不確定它的派生類的具體行為。例:
定義一個基類:class Animal//動物。它的函數為breathe()//呼吸。
再定義一個類class Fish//魚 。它的函數也為breathe()
再定義一個類class Sheep //羊。它的函數也為breathe()

為了簡化代碼,將Fish,Sheep定義成基類Animal的派生類。
然而Fish與Sheep的breathe不一樣,一個是在水中通過水來呼吸,一個是直接呼吸空氣。所以基類不能確定該如何定義breathe,所以在基類中只定義了一個virtual breathe,它是一個空的虛函數。具本的函數在子類中分別定義。程序一般運行時,找到類,如果它有基類,再找它的基類,最后運行的是基類中的函數,這時,它在基類中找到的是virtual標識的函數,它就會再回到子類中找同名函數。派生類也叫子類?;愐步懈割悺_@就是虛函數的產生,和類的多態性(breathe)的體現。

這里的多態性是指類的多態性。
函數的多態性是指一個函數被定義成多個不同參數的函數,它們一般被存在頭文件中,當你調用這個函數,針對不同的參數,就會調用不同的同名函數。例:Rect()//矩形。它的參數可以是兩個坐標點(point,point)也可能是四個坐標(x1,y1,x2,y2)這叫函數的多態性與函數的重載。

類的多態性,是指用虛函數和延遲綁定來實現的。函數的多態性是函數的重載。

一般情況下(沒有涉及virtual函數),當我們用一個指針/引用調用一個函數的時候,被調用的函數是取決于這個指針/引用的類型。即如果這個指針/引用是基類對象的指針/引用就調用基類的方法;如果指針/引用是派生類對象的指針/引用就調用派生類的方法,當然如果派生類中沒有此方法,就會向上到基類里面去尋找相應的方法。這些調用在編譯階段就確定了。

當設計到多態性的時候,采用了虛函數和動態綁定,此時的調用就不會在編譯時候確定而是在運行時確定。不在單獨考慮指針/引用的類型而是看指針/引用的對象的類型來判斷函數的調用,根據對象中虛指針指向的虛表中的函數的地址來確定調用哪個函數。

以上就是關于詳細解析C++多態的實現及原理,相信武林技術頻道提供的知識一定對大家是有用的,如果你還想學習更多的知識,建議你收藏武林技術頻道!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
在线亚洲欧美视频| 欧美日韩一区二区精品| 国产精品pans私拍| 亚洲2020天天堂在线观看| 精品国产一区二区三区久久狼5月| 伦理中文字幕亚洲| 国产精品老女人视频| 亚洲一区av在线播放| 欧美成人免费在线视频| 日韩动漫免费观看电视剧高清| 国产激情久久久久| 亚洲国产美女久久久久| 亚洲成人av片| 日本成人黄色片| 26uuu久久噜噜噜噜| 国产精品香蕉国产| 成人观看高清在线观看免费| 日韩精品中文字幕在线观看| 国产视频精品va久久久久久| 91黑丝高跟在线| 91午夜在线播放| 91香蕉国产在线观看| 亚洲成人精品av| 精品福利免费观看| 精品日韩中文字幕| 国产精品成人品| 日韩国产激情在线| 中文字幕亚洲综合久久筱田步美| 国产91精品最新在线播放| xvideos亚洲| 日韩动漫免费观看电视剧高清| 美女扒开尿口让男人操亚洲视频网站| 一本一道久久a久久精品逆3p| 欧美日韩激情小视频| 国产一区二区三区久久精品| 亚洲电影免费观看高清完整版在线观看| 中文字幕日韩在线视频| 色综合久久88色综合天天看泰| 精品福利樱桃av导航| 久久精品男人天堂| 91在线视频九色| 91精品综合久久久久久五月天| 国产精品欧美日韩一区二区| 欧美成人在线免费| 国产精品久久久久久av下载红粉| 国产精品福利在线观看网址| 亚洲夜晚福利在线观看| 亚洲白虎美女被爆操| 欧美大片在线看| 久久精品影视伊人网| 美女啪啪无遮挡免费久久网站| 91精品国产综合久久久久久久久| 国产va免费精品高清在线| 日韩久久精品电影| 国产欧美一区二区| 日韩免费高清在线观看| 欧美综合激情网| 久久久日本电影| 久久伊人精品天天| 亚洲成人1234| 91精品国产九九九久久久亚洲| 亚洲美女av黄| 国产精品久久久久久久7电影| 欧美日韩国产一区二区三区| 九九九久久国产免费| 久久免费视频在线观看| 亚洲色图17p| 欧美日韩中国免费专区在线看| 久久韩剧网电视剧| 亚洲最大的av网站| 亚洲国产精品va在线观看黑人| 亚洲毛片在线看| 日本精品久久久久久久| 国产偷亚洲偷欧美偷精品| 亚洲免费一在线| 国产在线a不卡| 欧美极品少妇全裸体| 久久人人爽亚洲精品天堂| 一区二区三区www| 欧美成人高清视频| 亚洲国产成人久久综合一区| 91国偷自产一区二区三区的观看方式| 97香蕉久久超级碰碰高清版| 92国产精品久久久久首页| 日韩欧美第一页| 成人免费观看网址| 欧美剧在线观看| 国产精品毛片a∨一区二区三区|国| 国产精品第三页| 欧美肥老妇视频| 欧美激情一区二区三区在线视频观看| 欧美日韩一区二区免费视频| 国产精品日韩欧美| www.欧美免费| 91免费精品视频| 日本高清不卡的在线| 中文字幕日韩av| 日韩av免费看网站| 人体精品一二三区| 亚洲aa在线观看| 亚洲精品在线看| 欧美亚洲成人网| 亚洲黄色www网站| 91精品啪在线观看麻豆免费| 日韩视频中文字幕| 国产成人精品一区二区三区| 久久人人97超碰精品888| 亚洲一区二区三区视频播放| 亚洲精品久久久久| 国产91成人video| 日韩中文字幕免费看| 亚洲精品影视在线观看| 亚洲综合av影视| 中文字幕亚洲综合久久筱田步美| 97国产真实伦对白精彩视频8| 久久精品视频在线观看| 精品国产一区二区三区久久狼5月| 国产一区二区三区视频| 国产99久久久欧美黑人| 日韩av手机在线观看| 国产精品免费在线免费| 欧美理论电影在线播放| 国产成人精品午夜| 亚洲欧美激情四射在线日| 国产精品一区久久| 国产成人精品国内自产拍免费看| 国产一区私人高清影院| 亚洲精品98久久久久久中文字幕| 欧美性在线视频| 国产免费一区二区三区在线观看| 欧美黑人视频一区| 亚洲精品一区二区三区不| 亚洲欧美制服丝袜| 国产精品美女久久久免费| 高清一区二区三区四区五区| 久久久久久av| 亚洲欧美国产精品专区久久| 国产日韩在线视频| 久久久精品久久久| 欧美亚洲激情视频| 97视频在线观看网址| 欧美成人精品在线播放| 成人国内精品久久久久一区| 国产精品免费久久久久影院| 日韩电影大片中文字幕| 日韩精品高清视频| 亚洲sss综合天堂久久| 欧美国产精品人人做人人爱| 日韩电影中文字幕| 中文字幕久久久| 亚洲成人亚洲激情| 亚洲欧美日韩高清| 欧美黑人性视频| 久久成人免费视频| 欧美精品一区三区| 日韩专区在线播放| 91免费版网站入口| 68精品久久久久久欧美| 国产精品久久久久久久app| 91精品久久久久久久久久久久久久| 欧美性xxxx极品高清hd直播| 97成人精品区在线播放| 欧美亚洲另类激情另类| 国产精品国产福利国产秒拍|