虛函式的一些總結

2021-05-24 23:10:08 字數 1744 閱讀 7993

虛函式與虛函式表:

1、每個類只有乙個虛函式表 如:

//  cout<<(int *)*((int *)&bb1)《其中對於//  cout<<(int *)*((int *)&bb1)<&bb1是取得物件的首位址,因為含有虛函式的物件中的首位址儲存的是虛指標的位址,所以&bb1就是虛指標的位址

(int *)&bb1是將虛函式的位址解釋為int型別

*((int *)&bb1)是取得虛函式指標所指向的虛函式表的首位址

(int *)*((int *)&bb1)即將這個虛函式表的首位址解釋為int型別輸出

這樣即bb1和bb2 的虛函式表的位址都相等,這個值就是虛函式表所在的位置。

可以看到在乙個含有虛函式的類中,物件指向的第乙個位址就是虛函式的虛指標的位址,因為虛指標是指向乙個類似一維陣列的首位址,所以對虛指標解引用之後得到的就是指向第乙個函式的位址,最後通過對得到的位址進行強制轉換為函式位址即可將函式位址賦值給f函式指標,最後通過函式的呼叫從而實現對虛函式表中的虛函式進行手動呼叫。同時可以看到獲得的位址和通過內聯彙編方式得到的位址是一致的。

通過本例也可看出,對於虛函式表的各個函式的位置的存放,首先存放的是父類的虛函式,該函式的位置按照在類中宣告的位置進行排序,對於子類中的虛函式,如果有對父類的虛函式進行覆蓋的,就將子類的虛函式替換父類的虛函式,這也就是為什麼能夠實現動態呼叫的原因,

base *b;

b=&d1;

b->f2();

對於d1,首先生成了虛函式表,然後將d1的位址賦值給b,也就是將b中的虛函式表中的f2函式的位址賦值為derived類中f2的位址,所以實現了動態的呼叫,如果沒有虛函式表,那麼就不會出現這樣對函式入口位址的覆蓋,但是如果用b去呼叫在derived中新定義的函式,而base中沒有的函式就不會將原來的函式覆蓋,同時如果子類中沒有對父類中的函式進行重新定義的話也不會進行覆蓋,如:

b->f1();

b->f4();//錯誤

用b去呼叫f1,因為子類沒有對其進行重新定義,所以呼叫的f1還是base的f1,然而對於b去呼叫f4,因為父類base中並沒有實現對f4的宣告,並不是base的成員,所以出錯。

3、多重繼承

對於多重繼承的含有虛函式的類來說,這個類並沒有自己生成乙個虛函式表而是用了父類的虛函式表,並且將自己的虛函式加到第乙個父類的虛函式表中,如:

得出的結果是

bb:8

b1:8

b2:8

dd:28

即每個父類都含有乙個虛函式指標,指向虛函式表的首位址,在dd類中,是由dd類生成乙個虛函式表,同時將bb的虛函式加進來,而b1和b2則保留著原來的虛函式表和虛函式指標。

4、不要在建構函式或者析構函式中呼叫虛函式

最後輸出的結果是:

in a

in a

說明並沒有發生動態呼叫。

從這生成b的物件b的內斂彙編中可以看到,首先會先呼叫a的建構函式然後再生成b的虛函式表,在a的建構函式中先生成a的虛函式表,用this指向虛函式表的首位址,此時並沒有生成b的虛函式表,所以此時呼叫a的show函式

對於析構函式中

首先,在return 0之後,呼叫b的析構函式

進入b的析構函式之後,首先先釋放虛函式表

最後再呼叫a的析構函式

當進入a的析構函式的時候,b的虛函式表也就不存在了,所以發生函式呼叫是實呼叫。

最後再總結乙個知識點就是,虛函式表是放在應用程式的常量區,和字串常量一樣。

關於c 的虛函式的一些總結

對於什麼時候子類會覆蓋父類中的函式 1 如果子類的東西和父類是一樣的,那麼肯定是子類覆蓋父類中的函式 2 如果子類的函式和父類的函式一模一樣,就是前面少了個virtual那麼,子類還是覆蓋父類的函式 3 如果子類和父類的函式返回值不一樣,那麼子類是不覆蓋父類的函式 4 最糾結的應該算是對於引數含有預...

虛函式的一些想像

看了vc中虛函式的實現原理,突然覺得很有意思,就寫了如下的 答案是 classb d 11,c 99 classa.showb b 11 classa.showa a 99 可能有些奇怪,但是想清楚了就覺得很自然了。首先classa和classb都擁有虛函式,對於這樣的類就會有乙個公共的虛函式表,採...

虛函式,純虛函式,多型的一些理解

定義乙個函式為虛函式,不代表函式為不被實現的函式。定義他為虛函式是為了允許用基類的指標來呼叫子類的這個函式。定義乙個函式為純虛函式,才代表函式沒有被實現。定義他是為了實現乙個介面,起到乙個規範的作用,規範繼承這個。類的程式設計師必須實現這個函式。1 為了方便使用多型特性,我們常常需要在基類中定義虛函...