在虛繼承中,虛基類是由最終的派生類初始化的,換句話說,最終派生類的建構函式必須要呼叫虛基類的建構函式。對最終的派生類來說,虛基類是間接基類,而不是直接基類。這跟普通繼承不同,在普通繼承中,派生類建構函式中只能呼叫直接基類的建構函式,不能呼叫間接基類的。
下面我們以菱形繼承為例來演示建構函式的呼叫:
#include
using
namespace std;
//虛基類a
classa;
a::a
(int a):
m_a(a)
//直接派生類b
classb:
virtual
public a
;b::b(
int a,
int b):
a(a),
m_b(b)
void b::
display
()//直接派生類c
classc:
virtual
public a
;c::c(
int a,
int c):
a(a),
m_c(c)
void c::
display
()//間接派生類d
classd:
public b,
public c
;d::d(
int a,
int b,
int c,
int d):
a(a),b(
90, b),c(
100, c),
m_d(d)
void d::
display
()int
main
()
執行結果:
m_a=10, m_b=20
m_a=30, m_c=40
m_a=50, m_b=60, m_c=70, m_d=80
請注意第 50 行**,在最終派生類 d 的建構函式中,除了呼叫 b 和 c 的建構函式,還呼叫了 a 的建構函式,這說明 d 不但要負責初始化直接基類 b 和 c,還要負責初始化間接基類 a。而在以往的普通繼承中,派生類的建構函式只負責初始化它的直接基類,再由直接基類的建構函式初始化間接基類,使用者嘗試呼叫間接基類的建構函式將導致錯誤。
現在採用了虛繼承,虛基類 a 在最終派生類 d 中只保留了乙份成員變數 m_a,如果由 b 和 c 初始化 m_a,那麼 b 和 c 在呼叫 a 的建構函式時很有可能給出不同的實參,這個時候編譯器就會犯迷糊,不知道使用哪個實參初始化 m_a。
為了避免出現這種矛盾的情況,c++ 乾脆規定必須由最終的派生類 d 來初始化虛基類 a,直接派生類 b 和 c 對 a 的建構函式的呼叫是無效的。在第 50 行**中,呼叫 b 的建構函式時試圖將 m_a 初始化為 90,呼叫 c 的建構函式時試圖將 m_a 初始化為 100,但是輸出結果有力地證明了這些都是無效的,m_a 最終被初始化為 50,這正是在 d 中直接呼叫 a 的建構函式的結果。
另外需要關注的是建構函式的執行順序。虛繼承時建構函式的執行順序與普通繼承時不同:在最終派生類的構造函式呼叫列表中,不管各個建構函式出現的順序如何,編譯器總是先呼叫虛基類的建構函式,再按照出現的順序呼叫其他的建構函式;而對於普通繼承,就是按照建構函式出現的順序依次呼叫的。
修改本例中第 50 行**,改變建構函式出現的順序:
d::d(
int a,
int b,
int c,
int d):b(
90, b),c(
100, c),
a(a),
m_d(d)
雖然我們將 a() 放在了最後,但是編譯器仍然會先呼叫 a(),然後再呼叫 b()、c(),因為 a() 是虛基類的建構函式,比其他建構函式優先順序高。
一般情況下是按照繼承的順序來呼叫建構函式。(與初始化建構函式時引數列表無關)
C 虛繼承時的建構函式的講解
在虛繼承中,虛基類是由最終的派生類初始化的,換句話說,最終派生類的建構函式必須要呼叫虛基類的建構函式。對最終的派生類來說,虛基類是間接基類 而不是直接基類。這跟普通繼承不同,在普通繼承中,派生類建構函式中只能呼叫直接基類的建構函式,不能呼叫間接基類的。下面我們以菱形繼承為例來演示建構函式的呼叫 in...
虛函式 虛繼承 C
關於虛表,我們就要用到乙個關鍵字 virtual,可以修飾函式,也可以修飾類。類的成員函式被virtual修飾之後,就成為了虛函式 修飾類,主要是虛繼承。在此之前,我們首先要了解乙個概念 物件模型,也就是說,乙個基類形成之後,裡面的成員是怎麼存放的,當派生類繼承基類之後,派生類的成員是怎麼存放的。我...
C 多繼承時的虛函式表結構
c 為了實現執行時的多型,引入了虛函式的概念。為了實現執行時多型的,其底層一般採用虛函式表來實現對虛函式的動態繫結,進而在基類物件的引用或指標在呼叫同名的虛函式時可以根據引用或指標指向物件的實際型別呼叫相應的函式。當類的繼承關係中沒有使用多繼承時,物件的虛函式表結構還相對簡單 然而繼承 現多整合時,...