柚子快報邀請碼778899分享:C++語法——詳解虛繼承
柚子快報邀請碼778899分享:C++語法——詳解虛繼承
目錄
一.什么是虛繼承
二.虛繼承原理
三.虛繼承使用注意事項
一.什么是虛繼承
所謂虛繼承(virtual)就是子類中只有一份間接父類的數(shù)據(jù)。該技術(shù)用于解決多繼承中的父類為非虛基類時出現(xiàn)的數(shù)據(jù)冗余問題,即菱形繼承問題。
小編用一張圖來表述一下:
如果是下圖這種非虛繼承,那么D類中就會出現(xiàn)兩個 int a,當(dāng)我們用D實例化對象調(diào)用a時,編譯會報錯,因為發(fā)生了混淆。除非指定類域B或C。當(dāng)然指定A也可以,因為默認(rèn)會從第一個父類中找。
?此時,D的實例化對象內(nèi)部結(jié)構(gòu)如下:
而當(dāng)我們使用虛繼承時,結(jié)構(gòu)是下圖這樣,D中只有一份父類A,當(dāng)我們調(diào)用A中數(shù)據(jù)時,并不會發(fā)生冗余。
?此時,D對象內(nèi)部結(jié)構(gòu)是這樣:
二.虛繼承原理
在上圖中,父類數(shù)據(jù)并不存放在虛繼承的子類中,那么子類怎么找到父類數(shù)據(jù)呢?
——在虛繼承的類中,會定義一個虛基表指針vbptr,指向虛基表。
而虛基表中會存在偏移量,這個量就是表的地址到父類數(shù)據(jù)地址的距離。
我們可以通過調(diào)試,找到虛基表指針和虛基表:
首先,我們?yōu)槊恳粋€數(shù)據(jù)賦值,以便觀察:
?之后,調(diào)用監(jiān)視,查看d對象地址和d中a數(shù)據(jù)地址:
?再通過d的地址查看內(nèi)存窗口,看d中內(nèi)存分布:
由此我們可以分析得到對象d及其內(nèi)部父類的內(nèi)存布局:
在這個我們可能會有個疑問,那這B和C中兩個是什么呢?
?這就是虛基表指針!
再通過內(nèi)存窗口,查看一下虛基表指針指向的地址,根據(jù)我們的了解應(yīng)該就是虛基表,而其中存有偏移量:
?而偏移量,就是虛基表指針地址到父類數(shù)據(jù)地址的距離,這里以b中虛基表為例:
到這里我們就能解釋一個問題:為什么bptr和cptr能夠找到并不位于自己內(nèi)部的變量a?
因為bptr和cptr都對d進(jìn)行了切片,當(dāng)各自尋找變量a時,會從自身的虛基表指針中找到虛基表,通過虛基表的偏移量找到變量a的地址,從而找到了變量a。?
畫圖解釋就是這樣:
?
三.虛繼承使用注意事項
當(dāng)使用虛繼承的時候,需要注意,虛繼承只有在多繼承時才有用。也就是說如果只有一層繼承關(guān)系或者是單繼承都將不起作用。
因為虛繼承是保證子類中只有一個間接父類,說簡單一點就是虛繼承只能在隔代繼承中起作用。
比如下面兩種情況即便虛繼承也沒有意義:
(1)是因為雖然虛繼承產(chǎn)生了虛基表和指針,但是class B并沒有子類,而虛繼承是用以保證子類只有一個間接父類class A。當(dāng)然話說回來,就算有子類、哪怕多個子類,也都體現(xiàn)不出虛繼承,因為虛繼承要求同一個子類的多個父類繼承自同一個間接父類,而該例只有一個父類class B。
(2)是因為雖然class C虛繼承了class B,但是class B是class A的非虛繼承,那么B中就會有一份A。而class D對A是虛繼承,就導(dǎo)致E在實例化時會存放一個對D而言公共的A。這樣E中還是存放了兩個A。調(diào)用變量時還是會混淆。
這樣說可能還有些難懂,那換個說法,class B中沒有虛基表指針,而D中有虛基表指針,當(dāng)E從D調(diào)用int a時會從虛基表指針找到公共區(qū)域的A,而E從B中找只會在B的區(qū)域內(nèi)找到int a。
畫圖表示E內(nèi)部結(jié)構(gòu)就是這樣:
?正確的繼承關(guān)系應(yīng)該是當(dāng)class A的子類繼承時,都是虛繼承,這才能保證當(dāng)有像class E這樣的間接子類定義時,class在其中都只會在公共區(qū)域有一份。對本例來說即class B是虛繼承。
?
?
只有兩種編程語言:一種是天天挨罵的,另一種是沒人用的——Bjarne Stroustrup(C++之父)
如有錯誤,敬請斧正
?
柚子快報邀請碼778899分享:C++語法——詳解虛繼承
文章鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。