




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、虛函數(shù)必須是基類的非靜態(tài)成員函數(shù),其訪問權(quán)限可以是protected或public,在基類的類定義中定義虛函數(shù)的一般形式: virtual 函數(shù)返回值類型 虛函數(shù)名(形參表) 函數(shù)體 派生類可以重置(“覆蓋”)基類的非虛函數(shù)嗎?Effective C+有條款說(shuō)明這個(gè)問題的條款37: 決不要重新定義繼承而來(lái)的非虛函數(shù)有兩種方法來(lái)看待這個(gè)問題:理論的方法和實(shí)踐的方法。讓我們先從實(shí)踐的方法開始。畢竟,理論家一般都很耐心。假設(shè)類D公有繼承于類B,并且類B中定義了一個(gè)公有成員函數(shù)mf。mf的參數(shù)和返回類型不重要,所以假設(shè)都為void。換句話說(shuō),我這么寫:class B public: void mf()
2、; .;class D: public B . ;甚至對(duì)B,D或mf一無(wú)所知,也可以定義一個(gè)類型D的對(duì)象x,D x; / x是類型D的一個(gè)對(duì)象那么,如果發(fā)現(xiàn)這么做:B *pB = &x; / 得到x的指針pB-mf(); / 通過(guò)指針調(diào)用mf和下面這么做的執(zhí)行行為不一樣:D *pD = &x; / 得到x的指針pD-mf(); / 通過(guò)指針調(diào)用mf你一定就會(huì)感到很驚奇。因?yàn)閮煞N情況下調(diào)用的都是對(duì)象x的成員函數(shù)mf,因?yàn)閮煞N情況下都是相同的函數(shù)和相同的對(duì)象,所以行為會(huì)相同,對(duì)嗎?對(duì),會(huì)相同。但,也許不會(huì)相同。特別是,如果mf是非虛函數(shù)而D又定義了自己的mf版本,行為就不會(huì)相同:class D:
3、public B public: void mf(); / 隱藏了B:mf; 參見條款50 .;pB-mf(); / 調(diào)用B:mfpD-mf(); / 調(diào)用D:mf行為的兩面性產(chǎn)生的原因在于,象B:mf和D:mf這樣的非虛函數(shù)是靜態(tài)綁定的(參見條款38)。這意味著,因?yàn)閜B被聲明為指向B的指針類型,通過(guò)pB調(diào)用非虛函數(shù)時(shí)將總是調(diào)用那些定義在類B中的函數(shù) - 即使pB指向的是從B派生的類的對(duì)象,如上例所示。相反,虛函數(shù)是動(dòng)態(tài)綁定的(再次參見條款38),因而不會(huì)產(chǎn)生這類問題。如果mf是虛函數(shù),通過(guò)pB或pD調(diào)用mf時(shí)都將導(dǎo)致調(diào)用D:mf,因?yàn)閜B和pD實(shí)際上指向的都是類型D的對(duì)象。所以,結(jié)論是,如
4、果寫類D時(shí)重新定義了從類B繼承而來(lái)的非虛函數(shù)mf,D的對(duì)象就可能表現(xiàn)出精神分裂癥般的異常行為。也就是說(shuō),D的對(duì)象在mf被調(diào)用時(shí),行為有可能象B,也有可能象D,決定因素和對(duì)象本身沒有一點(diǎn)關(guān)系,而是取決于指向它的指針?biāo)暶鞯念愋?。引用也?huì)和指針一樣表現(xiàn)出這樣的異常行為。實(shí)踐方面的論據(jù)就說(shuō)這么多。我知道你現(xiàn)在想知道的是,不能重新定義繼承而來(lái)的非虛函數(shù)的理論依據(jù)是什么。我很高興解答。條款35解釋了公有繼承的含義是 是一個(gè),條款36說(shuō)明了為什么 在一個(gè)類中聲明一個(gè)非虛函數(shù)實(shí)際上為這個(gè)類建立了一種特殊性上的不變性。如果將這些分析套用到類B、類D和非虛成員函數(shù)B:mf,那么, 適用于B對(duì)象的一切也適用于D對(duì)
5、象,因?yàn)槊總€(gè)D的對(duì)象 是一個(gè) B的對(duì)象。 B的子類必須同時(shí)繼承mf的接口和實(shí)現(xiàn),因?yàn)閙f在B中是非虛函數(shù)。那么,如果D重新定義了mf,設(shè)計(jì)中就會(huì)產(chǎn)生矛盾。如果D真的需要實(shí)現(xiàn)和B不同的mf,而且每個(gè)B的對(duì)象 - 無(wú)論怎么特殊 - 也真的要使用B實(shí)現(xiàn)的mf,那么,每個(gè)D將不 是一個(gè) B。這種情況下,D不能從B公有繼承。相反,如果D真的必須從B公有繼承,而且D真的需要和B不同的mf的實(shí)現(xiàn),那么,mf就沒有為B反映出特殊性上的不變性。這種情況下,mf應(yīng)該是虛函數(shù)。最后,如果每個(gè)D真的 是一個(gè) B,并且如果mf真的為B建立了特殊性上的不變性,那么,D實(shí)際上就不需要重新定義mf,也就決不能這樣做。不管采用
6、上面的哪一種論據(jù)都可以得出這樣的結(jié)論:任何條件下都要禁止重新定義繼承而來(lái)的非虛函數(shù)。派生類和基類的關(guān)系:1: 派生類對(duì)象可以使用基類的方法,條件是方法不是私有的。2: 基類指針可以在不進(jìn)行顯示類型轉(zhuǎn)換的情況下指向派生類對(duì)象;基類引用可以在不進(jìn)行顯示類型轉(zhuǎn)換的情況下引用派生類對(duì)象;但基類指針或引用只能用于調(diào)用基類的方法;3: 不可以將基類對(duì)象和地址賦給派生類引用和指針;4: 將派生類引用或者指針轉(zhuǎn)換成為基類引用或指針被稱為向上強(qiáng)制轉(zhuǎn)換,不需要進(jìn)行顯示類型轉(zhuǎn)換。將基類指針或引用轉(zhuǎn)換為派生類指針或引用成為向下強(qiáng)制轉(zhuǎn)換。5: 如果基類函數(shù)中沒有使用虛函數(shù),使用類型轉(zhuǎn)換才有靜態(tài)編譯;如果基類中有虛函數(shù),
7、則采用動(dòng)態(tài)聯(lián)編。編譯器處理虛函數(shù)的方法:給每個(gè)對(duì)象添加一個(gè)隱藏成員。隱藏成員中保存了一個(gè)指向函數(shù)的地址數(shù)組的指針。數(shù)組被成為虛函數(shù)表,虛函數(shù)表中存儲(chǔ)了為類對(duì)象進(jìn)行聲明的虛函數(shù)的地址。/*網(wǎng)上資料*/1. 用virtual關(guān)鍵字申明的函數(shù)叫做虛函數(shù),虛函數(shù)肯定是類的成員函數(shù)。2. 存在虛函數(shù)的類都有一個(gè)一維的虛函數(shù)表叫做虛表。類的對(duì)象有一個(gè)指向虛表開始的虛指針。虛表是和類對(duì)應(yīng)的,虛表指針是和對(duì)象對(duì)應(yīng)的。3. 多態(tài)性是一個(gè)接口多種實(shí)現(xiàn),是面向?qū)ο蟮暮诵?。分為類的多態(tài)性和函數(shù)的多態(tài)性。4. 多態(tài)用虛函數(shù)來(lái)實(shí)現(xiàn),結(jié)合動(dòng)態(tài)綁定。5. 純虛函數(shù)是虛函數(shù)再加上= 0。6. 抽象類是指包括至少一個(gè)純虛函數(shù)的類
8、。純虛函數(shù):virtual void breathe()=0;即抽象類!必須在子類實(shí)現(xiàn)這個(gè)函數(shù)!即先有名稱,沒內(nèi)容,在派生類實(shí)現(xiàn)內(nèi)容!虛標(biāo)指針的初始化:那么虛表指針在什么時(shí)候,或者說(shuō)在什么地方初始化呢?答案是在構(gòu)造函數(shù)中進(jìn)行虛表的創(chuàng)建和虛表指針的初始化。還記得構(gòu)造函數(shù)的調(diào)用順序嗎,在構(gòu)造子類對(duì)象時(shí),要先調(diào)用父類的構(gòu)造函數(shù),此時(shí)編譯器只“看到了”父類,并不知道后面是否后還有繼承者,它初始化父類對(duì)象的虛表指針,該虛表指針指向父類的虛表。當(dāng)執(zhí)行子類的構(gòu)造函數(shù)時(shí),子類對(duì)象的虛表指針被初始化,指向自身的虛表??偨Y(jié)(基類有虛函數(shù)):1. 每一個(gè)類都有虛表。2. 虛表可以繼承,如果子類沒有重寫虛函數(shù),那么子
9、類虛表中仍然會(huì)有該函數(shù)的地址,只不過(guò)這個(gè)地址指向的是基類的虛函數(shù)實(shí)現(xiàn)。如果基類有3個(gè)虛函數(shù),那么基類的虛表中就有三項(xiàng)(虛函數(shù)地址),派生類也會(huì)有虛表,至少有三項(xiàng),如果重寫了相應(yīng)的虛函數(shù),那么虛表中的地址就會(huì)改變,指向自身的虛函數(shù)實(shí)現(xiàn)。如果派生類有自己的虛函數(shù),那么虛表中就會(huì)添加該項(xiàng)。3. 派生類的虛表中虛函數(shù)地址的排列順序和基類的虛表中虛函數(shù)地址排列順序相同。 /虛基類 1, 一個(gè)類可以在一個(gè)類族中既被用作虛基類,也被用作非虛基類。 2, 在派生類的對(duì)象中,同名的虛基類只產(chǎn)生一個(gè)虛基類子對(duì)象,而某個(gè)非虛基類產(chǎn)生各自的子對(duì)象。 3, 虛基類子對(duì)象是由最派生類的構(gòu)造函數(shù)通過(guò)調(diào)用虛基類的構(gòu)造函數(shù)進(jìn)行
10、初始化的。 4, 最派生類是指在繼承結(jié)構(gòu)中建立對(duì)象時(shí)所指定的類。 5, 派生類的構(gòu)造函數(shù)的成員初始化列表中必須列出對(duì)虛基類構(gòu)造函數(shù)的調(diào)用;如果未列出,則表示使用該虛基類的缺省構(gòu)造函數(shù)。 6, 從虛基類直接或間接派生的派生類中的構(gòu)造函數(shù)的成員初始化列表中都要列出對(duì)虛基類構(gòu)造函數(shù)的調(diào)用。但只有用于建立對(duì)象的最派生類的構(gòu)造函數(shù)調(diào)用虛基類的構(gòu)造函數(shù),而該派生類的所有基類中列出的對(duì)虛基類的構(gòu)造函數(shù)的調(diào)用在執(zhí)行中被忽略,從而保證對(duì)虛基類子對(duì)象只初始化一次。 7, 在一個(gè)成員初始化列表中同時(shí)出現(xiàn)對(duì)虛基類和非虛基類構(gòu)造函數(shù)的調(diào)用時(shí),虛基類的構(gòu)造函數(shù)先于非虛基類的構(gòu)造函數(shù)執(zhí)行。虛基類:如果一個(gè)派生類有多個(gè)直接基
11、類,而這些直接基類又有一個(gè)共同的基類,則在最終的派生類中會(huì)保留該間接共同基類數(shù)據(jù)成員的多份同名成員?,F(xiàn)在,將類A聲明為虛基類,方法如下: class A /聲明基類A ; classB:virtual publicA /聲明類B是類A的公用派生類,A是B的虛基類 ; classC:virtual public A /聲明類C是類A的公用派生類,A是C的虛基類 ; 注意:虛基類并不是在聲明基類時(shí)聲明的,而是在聲明派生類時(shí),指定繼承方式時(shí)聲明的。因?yàn)橐粋€(gè)基類可以在生成一個(gè)派生類時(shí)作為虛基類,而在生成另一個(gè)派生類時(shí)不作為虛基類。 虛函數(shù) 1, 虛函數(shù)是非靜態(tài)的、非內(nèi)聯(lián)的成員函數(shù),而不能是友元函數(shù),但
12、虛函數(shù)可以在另一個(gè)類中被聲明為友元函數(shù)。 2, 虛函數(shù)聲明只能出現(xiàn)在類定義的函數(shù)原型聲明中,而不能在成員函數(shù)的函數(shù)體實(shí)現(xiàn)的時(shí)候聲明。 3, 一個(gè)虛函數(shù)無(wú)論被公有繼承多少次,它仍然保持其虛函數(shù)的特性。 4, 若類中一個(gè)成員函數(shù)被說(shuō)明為虛函數(shù),則該成員函數(shù)在派生類中可能有不同的實(shí)現(xiàn)。當(dāng)使用該成員函數(shù)操作指針或引用所標(biāo)識(shí)的對(duì)象時(shí),對(duì)該成員函數(shù)調(diào)用可采用動(dòng)態(tài)聯(lián)編。 5, 定義了虛函數(shù)后,程序中聲明的指向基類的指針就可以指向其派生類。在執(zhí)行過(guò)程中,該函數(shù)可以不斷改變它所指向的對(duì)象,調(diào)用不同版本的成員函數(shù),而且這些動(dòng)作都是在運(yùn)行時(shí)動(dòng)態(tài)實(shí)現(xiàn)的。虛函數(shù)充分體現(xiàn)了面向?qū)ο蟪绦蛟O(shè)計(jì)的動(dòng)態(tài)多態(tài)性。純虛函數(shù)版本的成員
13、函數(shù),而且這些動(dòng)作都是在運(yùn)行時(shí)動(dòng)態(tài)實(shí)現(xiàn)的。虛函數(shù)充分體現(xiàn)了面向?qū)ο蟪绦蛟O(shè)計(jì)的動(dòng)態(tài)多態(tài)性。 純虛函數(shù) 1, 當(dāng)在基類中不能為虛函數(shù)給出一個(gè)有意義的實(shí)現(xiàn)時(shí),可以將其聲明為純虛函數(shù),其實(shí)現(xiàn)留待派生類完成。 2, 純虛函數(shù)的作用是為派生類提供一個(gè)一致的接口。 3, 純虛函數(shù)不能實(shí)化化,但可以聲明指針。為什么構(gòu)造函數(shù)不能是虛函數(shù)2010-05-17 23:52:21|分類: c語(yǔ)言 |標(biāo)簽: |字號(hào)大中小訂閱 首先,讓我們假設(shè)他是虛的.當(dāng)我們?cè)跇?gòu)造函數(shù)中時(shí)并調(diào)用虛函數(shù).大家都知道,對(duì)于普通的成員函數(shù)虛函數(shù)的調(diào)用是在運(yùn)行時(shí)決定的(即晚捆綁.因?yàn)樵诰幾g時(shí)無(wú)法知道這個(gè)對(duì)象是屬于這個(gè)成員函數(shù)的那個(gè)類,還是屬于由
14、他派生出來(lái)的類).然而,在構(gòu)造函數(shù)中調(diào)用虛函數(shù)時(shí),他所調(diào)用的僅僅是本地版本.也就是說(shuō),虛函數(shù)在構(gòu)造函數(shù)中并不工作! 第一,在概念上,構(gòu)造函數(shù)的工作是把對(duì)象變成存在物。在任何構(gòu)造函數(shù)中,對(duì)象可能只是部分被形成我們只能知道基類已被初始化了,但不知道哪個(gè)類是從這個(gè)基類繼承來(lái)的。然而,虛函數(shù)是“向前”和“向外”進(jìn)行調(diào)用。它能調(diào)用在派生類中的函數(shù)。如果我們?cè)跇?gòu)造函數(shù)中也這樣做,那么我們所調(diào)用的函數(shù)可能操作還沒有被初始化的成員,這將導(dǎo)致災(zāi)難的發(fā)生。 第二,。當(dāng)一個(gè)構(gòu)造函數(shù)被調(diào)用時(shí),它做的首要的事情之一是初始化它的VPTR。因此,它只能知道它是“當(dāng)前”類的,而完全忽視這個(gè)對(duì)象后面是否還有繼承者。當(dāng)編譯器為這
15、個(gè)構(gòu)造函數(shù)產(chǎn)生代碼時(shí),它是為這個(gè)類的構(gòu)造函數(shù)產(chǎn)生代碼-既不是為基類,也不是為它的派生類(因?yàn)轭惒恢勒l(shuí)繼承它)。所以它使用的VPTR必須是對(duì)于這個(gè)類的VTABLE。而且,只要它是最后的構(gòu)造函數(shù)調(diào)用,那么在這個(gè)對(duì)象的生命期內(nèi),VPTR將保持被初始化為指向這個(gè)VTABLE。但如果接著還有一個(gè)更晚派生的構(gòu)造函數(shù)被調(diào)用,這個(gè)構(gòu)造函數(shù)又將設(shè)置VPTR指向它的VTABLE,等.直到最后的構(gòu)造函數(shù)結(jié)束。VPTR的狀態(tài)是由被最后調(diào)用的構(gòu)造函數(shù)確定的。這就是為什么構(gòu)造函數(shù)調(diào)用是從基類到更加派生類順序的另一個(gè)理由。 但是,當(dāng)這一系列構(gòu)造函數(shù)調(diào)用正發(fā)生時(shí),每個(gè)構(gòu)造函數(shù)都已經(jīng)設(shè)置VPTR指向它自己的VTABLE。如果
16、函數(shù)調(diào)用使用虛機(jī)制,它將只產(chǎn)生通過(guò)它自己的VTABLE的調(diào)用,而不是最后的VTABLE(所有構(gòu)造函數(shù)被調(diào)用后才會(huì)有最后的VTABLE)。另外,許多編譯器認(rèn)識(shí)到,如果在構(gòu)造函數(shù)中進(jìn)行虛函數(shù)調(diào)用,應(yīng)該使用早捆綁,因?yàn)樗鼈冎劳砝墝⒅粚?duì)本地函數(shù)產(chǎn)生調(diào)用。無(wú)論哪種情況,在構(gòu)造函數(shù)中調(diào)用虛函數(shù)都沒有結(jié)果。 所以,構(gòu)造函數(shù)不能是虛的,然而,對(duì)于析構(gòu)函數(shù)來(lái)說(shuō)他常常是,而且最好是虛的!這個(gè)此處暫時(shí)不議. 定義虛函數(shù)的限制: (1)非類的成員函數(shù)不能定義為虛函數(shù),類的成員函數(shù)中靜態(tài)成員函數(shù)和構(gòu)造函數(shù)也不能定義為虛函數(shù),但可以將析構(gòu)函數(shù)定義為虛函數(shù)。實(shí)際上,優(yōu)秀的程序員常常把基類的析構(gòu)函數(shù)定義為虛函數(shù)。因?yàn)?,?/p>
17、基類的析構(gòu)函數(shù)定義為虛函數(shù)后,當(dāng)利用delete刪除一個(gè)指向派生類定義的對(duì)象指針時(shí),系統(tǒng)會(huì)調(diào)用相應(yīng)的類的析構(gòu)函數(shù)。而不將析構(gòu)函數(shù)定義為虛函數(shù)時(shí),只調(diào)用基類的析構(gòu)函數(shù)。 (2)只需要在聲明函數(shù)的類體中使用關(guān)鍵字“virtual”將函數(shù)聲明為虛函數(shù),而定義函數(shù)時(shí)不需要使用關(guān)鍵字“virtual”。 (3)當(dāng)將基類中的某一成員函數(shù)聲明為虛函數(shù)后,派生類中的同名函數(shù)自動(dòng)成為虛函數(shù)。 (4)如果聲明了某個(gè)成員函數(shù)為虛函數(shù),則在該類中不能出現(xiàn)和這個(gè)成員函數(shù)同名并且返回值、參數(shù)個(gè)數(shù)、類型都相同的非虛函數(shù)。在以該類為基類的派生類中,也不能出現(xiàn)這種同名函數(shù)。 虛函數(shù)聯(lián)系到多態(tài),多態(tài)聯(lián)系到繼承。所以本文中都是在繼
18、承層次上做文章。沒了繼承,什么都沒得談虛函數(shù)是如何做到的(如果你沒有看過(guò)Inside The C+ Object Model這本書,但又急切想知道,那你就應(yīng)該從這里開始) 虛函數(shù)是如何做到因?qū)ο蟮牟煌{(diào)用其相應(yīng)的函數(shù)的呢?現(xiàn)在我們就來(lái)剖析虛函數(shù)。我們先定義兩個(gè)類 class A /虛函數(shù)示例代碼 public: virtual void fun()cout1endl; virtual void fun2()cout2endl; ; class B:public A public: void fun()cout3endl; void fun2()cout4fun(); 毫無(wú)疑問,調(diào)用了A:fu
19、n(),但是A:fun()是如何被調(diào)用的呢?它像普通函數(shù)那樣直接跳轉(zhuǎn)到函數(shù)的代碼處嗎?No,其實(shí)是這樣的,首先是取出vptr的值,這個(gè)值就是vtbl的地址,再根據(jù)這個(gè)值來(lái)到vtbl這里,由于調(diào)用的函數(shù)A:fun()是第一個(gè)虛函數(shù),所以取出vtbl第一個(gè)slot里的值,這個(gè)值就是A:fun()的地址了,最后調(diào)用這個(gè)函數(shù)。現(xiàn)在我們可以看出來(lái)了,只要vptr不同,指向的vtbl就不同,而不同的vtbl里裝著對(duì)應(yīng)類的虛函數(shù)地址,所以這樣虛函數(shù)就可以完成它的任務(wù)。 而對(duì)于class A和class B來(lái)說(shuō),他們的vptr指針存放在何處呢?其實(shí)這個(gè)指針就放在他們各自的實(shí)例對(duì)象里。由于class A和cla
20、ss B都沒有數(shù)據(jù)成員,所以他們的實(shí)例對(duì)象里就只有一個(gè)vptr指針。通過(guò)上面的分析,現(xiàn)在我們來(lái)實(shí)作一段代碼,來(lái)描述這個(gè)帶有虛函數(shù)的類的簡(jiǎn)單模型。 #include using namespace std; /將上面“虛函數(shù)示例代碼”添加在這里 int main() void (*fun)(A*); A *p=new B; long lVptrAddr; memcpy(&lVptrAddr,p,4); memcpy(&fun,reinterpret_cast(lVptrAddr),4); fun(p); delete p; system(pause); 用VC或Dev-C+編譯運(yùn)行一下,看看結(jié)果
21、是不是輸出3,如果不是,那么太陽(yáng)明天肯定是從西邊出來(lái)?,F(xiàn)在一步一步開始分析 void (*fun)(A*); 這段定義了一個(gè)函數(shù)指針名字叫做fun,而且有一個(gè)A*類型的參數(shù),這個(gè)函數(shù)指針待會(huì)兒用來(lái)保存從vtbl里取出的函數(shù)地址 A* p=new B; new B是向內(nèi)存(內(nèi)存分5個(gè)區(qū):全局名字空間,自由存儲(chǔ)區(qū),寄存器,代碼空間,棧)自由存儲(chǔ)區(qū)申請(qǐng)一個(gè)內(nèi)存單元的地址然后隱式地保存在一個(gè)指針中.然后把這個(gè)地址賦值給A類型的指針P. . long lVptrAddr; 這個(gè)long類型的變量待會(huì)兒用來(lái)保存vptr的值 memcpy(&lVptrAddr,p,4); 前面說(shuō)了,他們的實(shí)例對(duì)象里只有vp
22、tr指針,所以我們就放心大膽地把p所指的4bytes內(nèi)存里的東西復(fù)制到lVptrAddr中,所以復(fù)制出來(lái)的4bytes內(nèi)容就是vptr的值,即vtbl的地址 現(xiàn)在有了vtbl的地址了,那么我們現(xiàn)在就取出vtbl第一個(gè)slot里的內(nèi)容 memcpy(&fun,reinterpret_cast(lVptrAddr),4); 取出vtbl第一個(gè)slot里的內(nèi)容,并存放在函數(shù)指針fun里。需要注意的是lVptrAddr里面是vtbl的地址,但lVptrAddr不是指針,所以我們要把它先轉(zhuǎn)變成指針類型 fun(p); 這里就調(diào)用了剛才取出的函數(shù)地址里的函數(shù),也就是調(diào)用了B:fun()這個(gè)函數(shù),也許你發(fā)現(xiàn)
23、了為什么會(huì)有參數(shù)p,其實(shí)類成員函數(shù)調(diào)用時(shí),會(huì)有個(gè)this指針,這個(gè)p就是那個(gè)this指針,只是在一般的調(diào)用中編譯器自動(dòng)幫你處理了而已,而在這里則需要自己處理。 delete p; 釋放由p指向的自由空間; system(pause); 屏幕暫停; 如果調(diào)用B:fun2()怎么辦?那就取出vtbl的第二個(gè)slot里的值就行了 memcpy(&fun,reinterpret_cast(lVptrAddr+4),4); 為什么是加4呢?因?yàn)橐粋€(gè)指針的長(zhǎng)度是4bytes,所以加4?;蛘適emcpy(&fun,reinterpret_cast(lVptrAddr)+1,4); 這更符合數(shù)組的用法,因?yàn)閘
24、VptrAddr被轉(zhuǎn)成了long*型別,所以+1就是往后移sizeof(long)的長(zhǎng)度 三, 以一段代碼開始#include using namespace std; class A /虛函數(shù)示例代碼2 public: virtual void fun() coutA:funendl; virtual void fun2()coutA:fun2endl; ; class B:public A public: void fun() coutB:funendl; void fun2() coutB:fun2*fun)(); fun = &A:fun2; (p-*fun)(); delete p;
25、 system(pause); 你能估算出輸出結(jié)果嗎?如果你估算出的結(jié)果是A:fun和A:fun2,呵呵,恭喜恭喜,你中圈套了。其實(shí)真正的結(jié)果是B:fun和B:fun2,如果你想不通就接著往下看。給個(gè)提示,&A:fun和&A:fun2是真正獲得了虛函數(shù)的地址嗎? 首先我們回到第二部分,通過(guò)段實(shí)作代碼,得到一個(gè)“通用”的獲得虛函數(shù)地址的方法 #include using namespace std; /將上面“虛函數(shù)示例代碼2”添加在這里 void CallVirtualFun(void* pThis,int index=0) void (*funptr)(void*); long lVptrA
26、ddr; memcpy(&lVptrAddr,pThis,4); memcpy(&funptr,reinterpret_cast(lVptrAddr)+index,4); funptr(pThis); /調(diào)用 int main() A* p=new B; CallVirtualFun(p); /調(diào)用虛函數(shù)p-fun() CallVirtualFun(p,1);/調(diào)用虛函數(shù)p-fun2() system(pause); CallVirtualFun方法現(xiàn)在我們擁有一個(gè)“通用”的CallVirtualFun方法。 這個(gè)通用方法和第三部分開始處的代碼有何聯(lián)系呢?聯(lián)系很大。由于A:fun()和A:fu
27、n2()是虛函數(shù),所以&A:fun和&A:fun2獲得的不是函數(shù)的地址,而是一段間接獲得虛函數(shù)地址的一段代碼的地址,我們形象地把這段代碼看作那段CallVirtualFun。編譯器在編譯時(shí),會(huì)提供類似于CallVirtualFun這樣的代碼,當(dāng)你調(diào)用虛函數(shù)時(shí),其實(shí)就是先調(diào)用的那段類似CallVirtualFun的代碼,通過(guò)這段代碼,獲得虛函數(shù)地址后,最后調(diào)用虛函數(shù),這樣就真正保證了多態(tài)性。同時(shí)大家都說(shuō)虛函數(shù)的效率低,其原因就是,在調(diào)用虛函數(shù)之前,還調(diào)用了獲得虛函數(shù)地址的代碼。關(guān)于如何在派生類的虛函數(shù)中調(diào)用被覆蓋掉的同名基類的虛函數(shù),即Java中的super在C+中的使用方法 Posted on
28、 2009-03-25 23:00 魔kyo 閱讀(895) 評(píng)論(1) 編輯收藏 引用 很久沒寫B(tài)LOG了,這是在設(shè)計(jì)實(shí)現(xiàn)中遇到的一個(gè)實(shí)際問題,就是類似下面的Java代碼在C+中如何實(shí)現(xiàn)的問題1importjava.io.*;2importjava.util.*;34classB56publicvoidPrint()78System.out.println(B);9101112classDextendsB1314publicvoidPrint()1516super.Print();17System.out.println(D);18192021classMain2223publicstati
29、cvoidmain(Stringargs)2425Bobj=newD();26obj.Print();272829有時(shí)候我們需要將所有子類方法中都會(huì)執(zhí)行的代碼放到基類方法中,或者可能這些代碼是操作基類成員的本來(lái)就不應(yīng)該放在子類,在Java中可以通過(guò)super來(lái)調(diào)用被覆蓋掉的父類中的方法。我居然以為C+無(wú)法實(shí)現(xiàn)這一點(diǎn),實(shí)在是太無(wú)知了。Google之后才知道原來(lái)可以這樣用1#include2usingnamespacestd;34classB56public:7virtualvoidPrint()89puts(B);1011;1213classD:publicB1415public:16virtu
30、alvoidPrint()1718B:Print();19puts(D);2021;2223intmain()2425B*p=newD();26p-Print();2728讓我認(rèn)為C+無(wú)法實(shí)現(xiàn)這點(diǎn)是有理由的,我們把virtual去掉看以下的代碼1#include2usingnamespacestd;34classB56public:7voidPrint()89puts(B);1011;1213classD:publicB1415public:16voidPrint()1718(B*)(this)-Print();19puts(D);2021;2223intmain()2425D*p=newD(
31、);26p-Print();2728這段代碼是可以正確實(shí)行的,因?yàn)闆]有virtual,D類中的Print并沒有覆蓋B類中的Print,只是同名而已(這是很不好的做法,容易產(chǎn)生誤解),所以這時(shí)不存在運(yùn)行時(shí)綁定,我只好D* p=new D(); 我認(rèn)為子類對(duì)象在內(nèi)存中是這樣的但是virtual的方法會(huì)被覆蓋掉,也就是說(shuō)對(duì)于D類的對(duì)象,已經(jīng)沒有B類的Print方法了。所以如果加上virtual我們就無(wú)法正確執(zhí)行上面的代碼了??墒荁:Print(); 又是如何實(shí)現(xiàn)的呢?Stockbroker GrapevineTime Limit: 1000MSMemory Limit: 10000KTotal Su
32、bmissions: 17482Accepted: 9439DescriptionStockbrokers are known to overreact to rumours. You have been contracted to develop a method of spreading disinformation amongst the stockbrokers to give your employer the tactical edge in the stock market. For maximum effect, you have to spread the rumours i
33、n the fastest possible way. Unfortunately for you, stockbrokers only trust information coming from their Trusted sources This means you have to take into account the structure of their contacts when starting a rumour. It takes a certain amount of time for a specific stockbroker to pass the rumour on
34、 to each of his colleagues. Your task will be to write a program that tells you which stockbroker to choose as your starting point for the rumour, as well as the time it will take for the rumour to spread throughout the stockbroker community. This duration is measured as the time needed for the last
35、 person to receive the information.InputYour program will input data for different sets of stockbrokers. Each set starts with a line with the number of stockbrokers. Following this is a line for each stockbroker which contains the number of people who they have contact with, who these people are, an
36、d the time taken for them to pass the message to each person. The format of each stockbroker line is as follows: The line starts with the number of contacts (n), followed by n pairs of integers, one pair for each contact. Each pair lists first a number referring to the contact (e.g. a 1 means person number one in the set), followed by the time in minutes taken to pass a message to that person.
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年注會(huì)考試中的知識(shí)點(diǎn)整合與早期規(guī)劃的必要性探討試題及答案
- 電聲器件在智能語(yǔ)音助手平板中的應(yīng)用考核試卷
- 2025年注會(huì)考生反饋試題及答案
- 糧食大豆生產(chǎn)方案范本
- 2025年會(huì)計(jì)核算方法試題及答案
- 財(cái)務(wù)報(bào)表分析技巧試題及答案2025
- 2024年行政管理師考前準(zhǔn)備試題及答案
- 項(xiàng)目管理財(cái)務(wù)知識(shí)考題試題及答案
- 項(xiàng)目管理主動(dòng)溝通試題及答案
- 石棉水泥制品國(guó)際貿(mào)易實(shí)務(wù)考核試卷
- 2024年招錄考試-軍轉(zhuǎn)干考試近5年真題集錦(頻考類試題)帶答案
- 綿陽(yáng)小升初數(shù)學(xué)試題-(綿中英才學(xué)校)
- 2024年共青團(tuán)團(tuán)課考試測(cè)試題庫(kù)及答案
- 數(shù)字編碼(拔尖練習(xí))2024-2025學(xué)年人教版數(shù)學(xué)三年級(jí)上冊(cè)
- 逐夢(mèng)紅領(lǐng)巾爭(zhēng)做新時(shí)代好隊(duì)員培訓(xùn)課件
- 退休人員出國(guó)探親申請(qǐng)書
- DB43-T 3020-2024 不動(dòng)產(chǎn)登記業(yè)務(wù)規(guī)范
- 2024年全國(guó)職業(yè)院校技能大賽高職組(環(huán)境檢測(cè)與監(jiān)測(cè)賽項(xiàng))考試題庫(kù)(含答案)
- 蘇科版(2024)八年級(jí)下冊(cè)物理期末復(fù)習(xí)重要知識(shí)點(diǎn)考點(diǎn)提綱
- 2024木托盤賣買合同協(xié)議書范本
- 《習(xí)作:漫畫的啟示》學(xué)習(xí)任務(wù)群教學(xué)課件-課例1
評(píng)論
0/150
提交評(píng)論