• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 195,  comments - 30,  trackbacks - 0

            http://hi.baidu.com/walkman0000/blog/item/87b316ce24edd53eb600c8fa.html
            我們先看一個(gè)例子:
            1- 1

            #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的程序中沒有定義虛函數(shù)。考慮下例1-1的程序執(zhí)行的結(jié)果是什么?

                   答案是輸出:animal breathe

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

            1、 編譯的角度

            C++編譯器在編譯的時(shí)候,要確定每個(gè)對象調(diào)用的函數(shù)的地址,這稱為早期綁定(early binding),當(dāng)我們將fish類的對象fh的地址賦給pAn時(shí),C++編譯器進(jìn)行了類型轉(zhuǎn)換,此時(shí)C++編譯器認(rèn)為變量pAn保存的就是animal對象的地址。當(dāng)在main()函數(shù)中執(zhí)行pAn->breathe()時(shí),調(diào)用的當(dāng)然就是animal對象的breathe函數(shù)。

            2、 內(nèi)存模型的角度

            我們給出了fish對象內(nèi)存模型,如下圖所示:

             

             

            1- 1 fish類對象的內(nèi)存模型

             

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

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

            前面輸出的結(jié)果是因?yàn)榫幾g器在編譯的時(shí)候,就已經(jīng)確定了對象調(diào)用的函數(shù)的地址,要解決這個(gè)問題就要使用遲綁定(late binding)技術(shù)。當(dāng)編譯器使用遲綁定時(shí),就會在運(yùn)行時(shí)再去確定對象的類型以及正確的調(diào)用函數(shù)。而要讓編譯器采用遲綁定,就要在基類中聲明函數(shù)時(shí)使用virtual關(guān)鍵字(注意,這是必須的,很多學(xué)員就是因?yàn)闆]有使用虛函數(shù)而寫出很多錯誤的例子),這樣的函數(shù)我們稱為虛函數(shù)。一旦某個(gè)函數(shù)在基類中聲明為virtual,那么在所有的派生類中該函數(shù)都是virtual,而不需要再顯式地聲明為virtual

            下面修改例1-1的代碼,將animal類中的breathe()函數(shù)聲明為virtual,如下:

            1- 2

            #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();

            }

            大家可以再次運(yùn)行這個(gè)程序,你會發(fā)現(xiàn)結(jié)果是“fish bubble”,也就是根據(jù)對象的類型調(diào)用了正確的函數(shù)。

            那么當(dāng)我們將breathe()聲明為virtual時(shí),在背后發(fā)生了什么呢?

            編譯器在編譯的時(shí)候,發(fā)現(xiàn)animal類中有虛函數(shù),此時(shí)編譯器會為每個(gè)包含虛函數(shù)的類創(chuàng)建一個(gè)虛表(即vtable),該表是一個(gè)一維數(shù)組,在這個(gè)數(shù)組中存放每個(gè)虛函數(shù)的地址。對于例1-2的程序,animalfish類都包含了一個(gè)虛函數(shù)breathe(),因此編譯器會為這兩個(gè)類都建立一個(gè)虛表,如下圖所示:

            1- 2 animal類和fish類的虛表

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

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

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

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

            總結(jié)(基類有虛函數(shù)):

            1、 每一個(gè)類都有虛表。

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

            3、 派生類的虛表中虛函數(shù)地址的排列順序和基類的虛表中虛函數(shù)地址排列順序相同。

            posted on 2011-04-08 22:23 luis 閱讀(182) 評論(0)  編輯 收藏 引用

            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            <2011年4月>
            272829303112
            3456789
            10111213141516
            17181920212223
            24252627282930
            1234567

            常用鏈接

            留言簿(3)

            隨筆分類

            隨筆檔案

            文章分類

            文章檔案

            友情鏈接

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            欧美大香线蕉线伊人久久| 99久久99久久精品国产片果冻| 性高朝久久久久久久久久| 久久免费大片| 人妻精品久久无码专区精东影业| 国产91色综合久久免费分享| 久久久久噜噜噜亚洲熟女综合| 99久久99久久精品国产片果冻| 天天综合久久久网| 久久福利资源国产精品999| 蜜臀av性久久久久蜜臀aⅴ| 久久精品18| 狠狠色丁香久久婷婷综合五月| 久久亚洲国产成人影院网站| 久久久噜噜噜久久熟女AA片| 日韩美女18网站久久精品| 国产精品久久永久免费| 久久人人添人人爽添人人片牛牛| 99久久精品免费看国产一区二区三区 | 狠狠干狠狠久久| 久久久亚洲欧洲日产国码是AV| 99久久无码一区人妻a黑| 亚洲国产成人久久一区WWW| 欧美激情精品久久久久| 午夜久久久久久禁播电影| 无码精品久久一区二区三区| 91精品国产91久久| 精品人妻久久久久久888| 伊人久久大香线焦AV综合影院| 亚洲乱码日产精品a级毛片久久 | 久久人人爽人人爽人人片AV高清| 久久精品国产亚洲综合色| 久久99精品久久久久子伦| 97精品依人久久久大香线蕉97| 久久只有这里有精品4| 看全色黄大色大片免费久久久| 久久国产免费直播| 久久精品国产亚洲AV不卡| 久久黄视频| 99精品国产免费久久久久久下载 | 久久久国产打桩机|