我們在設計類的時候經常會遇到這樣一種情況,即同乙個方法在派生類和基類中實現不同的功能,這種行為稱為多型。
要實現這種效果,有兩個方法:
1、在派生類中重新定義基類的方法
2、使用虛函式
先看第一種方法
#include
#include
using
namespace std;
class
father};
class
son:
public father};
intmain
(void
)
在派生類裡重新定義play()之後,子類和父類的play()方法就不一樣了。但是這樣做有個缺陷,比如有乙個基類指標,讓後讓它指向乙個派生類物件
father father;
son son;
father* p[2]
=;p[0]-
>
play()
; p[1]
->
play()
;
這樣會發生什麼呢?指向派生類物件的基類指標呼叫的play()方法仍是基類的play(),也就是說這兩個方法都是基類的,並沒有實現多型。
這時候就要用到虛函式了
class
father
};
將父類的方法宣告為虛的,上面的問題就解決了。
宣告了虛方法之後,將根據基類指標指向的物件的型別呼叫該方法,而不是根據指標的型別去呼叫。
這樣做有什麼意義呢?有些時候在編譯之前,物件的型別並不確定,比如在程式執行的過程中建立了乙個物件,但你並不知道它的型別,所以編譯器必須要選擇正確的方法,這就是動態聯編。
再補充一點:宣告乙個虛析構函式只有好處沒有壞處。
father* p=
new son;..
....
delete p;
如果析構函式不是虛的,只會釋放son物件中father占用的記憶體。如果析構函式是虛的,會先呼叫~son()再呼叫 ~father()。
很多人都知道多型,虛函式,動態聯編,但是具體的原理呢,為什麼虛函式能實現動態聯編?
其實不知道也沒關係,不影響你使用虛函式,但知道了能裝逼。(末尾有彩蛋!)
要知道虛函式的原理,必須對指標有深入的了解,接下來的內容有點燒腦。
class
father
virtual
void
fun2()
virtual
void
fun3()
virtual
void
fun4()
void
fun5()
int x =10;
int y =20;
static
int z;};
int father::z =30;
intmain
(void
)
這裡有乙個很關鍵的問題,father有多大?
在32位系統上是12,64位系統是16,因為father物件中只有乙個指向虛函式表的指標和兩個資料成員x,y。其他東西都是屬於類的,並不在物件裡面。那麼知道了這一點之後,看圖:
p指向的是物件的位址,那麼怎麼把虛函式表指標取出來?
int
* vptr =
(int*)
*(p)
;//虛函式表指標
直接解引就可以了。
現在拿到了虛函式表指標,下一步怎麼辦?
接下來就可以訪問虛函式表,獲取虛函式的位址。
方法也是直接解引,那麼型別是什麼呢?函式的位址,那就是乙個函式指標,所以接下來定義乙個函式指標型別,然後強轉。
虛函式表指標,虛函式表
對c 了解的人都應該知道虛函式 virtual function 是通過一張虛函式表 virtual table 來實現的。簡稱為v table。在這個表中,主是要乙個類的虛函式的位址表,這張表解決了繼承 覆蓋的問題,保證其容真實反應實際的函式。這樣,在有虛函式的類的例項中這個表被分配在了 這個例項...
虛函式表和虛函式表的指標
有虛函式的類都有乙個虛函式表,它是實現多型的關鍵。虛函式表可以繼承,如果子類沒有重寫虛函式,那麼子類虛函式表中仍然會有該函式的位址,只不過這個位址指向的是基類的函式實現。如果子類重寫了相應的虛函式,那麼虛函式表中的位址就會改變,指向自身的函式實現。如果派生類中有自己的虛函式,那麼虛函式表中會新增該項...
虛函式之虛函式表
多型性可分為兩類 靜態多型和動態多型。函式過載和運算子過載實現的多型屬於靜態多型,動態多型性是通過虛函式實現的。每個含有虛函式的類有一張虛函式表 vtbl 表中每一項是乙個虛函式的位址,也就是說,虛函式表的每一項是乙個虛函式的指標。沒有虛函式的c 類,是不會有虛函式表的。兩張圖 簡單例子 inclu...