关于多态
多態又分為靜態多態和動態多態。
靜態多態其實就是函數重載,動態多態就是一般我們所說的多態。
多態作為面向對象的三大特征之一,需要另外兩大特征:封裝、繼承的支持。
本文主要講講,我看了一點《深度探索C++模型》總結多態是怎樣實現的。
1.C++內存布局
C++對象主要可以有如下幾種成員:
(1)數據成員
①static數據成員
②nonstatic數據成員
(2)函數成員
①static函數成員
②nonstatic函數成員
③virtual函數成員
其中,nonstatic數據成員和virtual函數成員是要占用,變量的內存空間的。
其他成員都是使用一個公共的靜態區域。
具體地講,每一個類對象會為其每一個nonstatic數據成員持有一個指針,而對于其所有的虛函數,會持有1個指針vptr,這個指針指向一個表vtbl,這個表里每一個位置也是一個指針,指向該類的各個虛函數。
2.多態的實現
在定義父類和子類的時候,父類對象的與子類對象都擁有vptr這個成員,但是父類的vptr指向的vtbl里面的指針指向的虛函數是父類的版本。
子類的vptr指向的btbl里面的指針指向的虛函數是子類的版本。
這個vptr指向的btbl里面放的虛函數是哪一個版本是由構造器、拷貝復制運算符決定的。
如果是構造一個父類對象,就是指向父類的虛函數,如果構造一個子類對象就是指向一個子類的虛函數。
如果把一個子類對象,強制轉換為一個父類對象,其vptr也會變為指向父類虛函數的。
但是當我們把一個父類的指針賦值一個指向了子類的指針,這個時候首先會發生切割,將子類多出來的成員給切割掉。
因為不同類型的指針,會限定從該地址開始的后面的范圍的內存布局。
但是由于指針賦值,并沒有調用子類或者父類的構造器,并且vptr是子類對象與父類對象所共有的,所以并沒有被切割。
所以這個時候如果一個指針開始指向了一個子類對象,現在通過這個指針調用的虛函數版本就是子類的,雖然這個指針現在是一個父類指針。
注意:
2016/06/01更新
我用G++ 4.9.2編譯器驗證出來的一些信息
1.vtbl源自父類的會不會也用來填裝子類新加入的虛函數
會。
如果父類沒有虛函數,子類有就會在子類額外區間多一個vptr
如果父類有虛函數,父類區間的vptr指向的vtbl也會存放子類新建的虛函數。
2.vtbl會不會同時用來記錄虛基類信息
不會。有一個vptr,有一個vbtr
但是如果虛基類里面沒有數據成員的話,是不會產生一個vbtr用于訪問數據成員的。
3.如果是多重繼承的虛擬基類是采用多少個vbtr
多重繼承了多少個虛擬基類就會有多少個vbtr
4.如果是多階繼承
對于一階,如果有單獨定義的變量,那么每一階就會產生一個vbtr
總結:
GCC的這些貌似少了很多優化,但是它好像是講vptr和vbtr都用一個指針來實現的,然后進行了相應地處理。
總結
- 上一篇: 用户线程和内核线程之间的区别
- 下一篇: 关于二叉树