虛擬繼承,虛基類

2021-05-22 11:54:36 字數 2711 閱讀 8426

文章出處:http://blog.csdn.net/skylor/archive/2009/03/26/4025698.aspx

虛擬繼承與虛基類實際上是說了同一件事,只是不同的書表達不同,在這裡還是推薦虛擬繼承這種說法(因為總有人問虛基類是什麼,這裡可以解釋為虛基類就是虛擬繼承,一種繼承的方式,有的書偏要把乙個動作寫成乙個名詞,不負責任)。虛擬繼承是c++繼承的乙個特殊方法,用來達到特殊的目的。要達到什麼目的呢?那就是避免繼承機制下的二義性問題(二義性:程式產生兩種或多種可能,把編譯器搞的不知所措)

繼承機制下的二義性一般體現在兩種情況下,要介紹的虛擬繼承主要解決了其中第二種情況的二義性問題,不妨把兩種情況都簡單說一說:

1.  第一種情況:由多個基類同名成員產生的二義性

class a  //定義乙個類a

public:

a()private:

int main(void)

c c;

c.print(); -----------------------------------mark 1

getchar();

return 0;

如上圖**所示,主程式main()在執行到mark1標記時產生了二義性,c物件有兩個基類,編譯器不知道該呼叫a的print()還是b的print(),這個二義性的產生的解決辦法虛擬繼承無關,需要用作用域符號來解決,即將c.print()修改為c a::print(); 那麼就呼叫a的print方法,也就是告訴他要呼叫的方法在哪個基類裡。

2.  第二種情況:由多個父類的共同基類產生的二義性

a是b, c的共同基類,d繼承於b, c

class a

public:

a(){ cout<<"a called"class b : public a

public:

b(){ cout<<"b called"class c : public a

public:

c(){ cout<<"c called"class d : public b, public c

public:

d(){ cout<<"d called"int main( void )

d d;

d.print(); -----------------------------------mark 2

getchar();

return 0;

如圖和**所示:主程式main在執行到mark2標記時產生了二義性,雖然只有基類a中有print(),但是繼承的路線有兩條,編譯器不知道從b路線向上找還是從c向上找,一樣會出錯,這裡可以用第一種方法用到的作用域說明符號即將d.print()改為d.b::print(),告訴編譯器是從b繼承下來的,當然,也可改為d.c::print()。除了這種解決方案,還有另外一種解決方案,就是:運用虛擬繼承機制。實際上造成上邊的二義性的根本原因是在這種繼承的特殊模式下,a這個父類分別伴隨b和c產生了兩個拷貝,在呼叫拷貝中的方法時產生了矛盾,到底是呼叫哪乙個拷貝中的print()呢?於是,所有人都會想,要是只有乙個拷貝就好了,就沒有矛盾了,虛擬繼承就提供了這種機制,按上面的例子,只需修改b和c對a的繼承方式,即加乙個關鍵字virtual

class b : virtual public a

public:

b(){ cout<<"b called"class c : virtual public a

public:

c(){ cout<<"c called"這就相當於說,在沒有a類的拷貝時就構造乙個,如果已經有了,就用已經有的那乙個,這樣一來,拷貝只有乙份了,二義性消除了。

虛擬繼承不多說了,最後再補充點關於繼承的東西。實際上繼承框架性的東西不多,乙個訪問控制,乙個呼叫數序,把這兩個搞清楚,再把上面的弄明白,就差我下面要說的一件事了,

有個地方叫繼承的支配規則:

派生類中設定了基類中同名的成員,就是說基類的成員的名字在派生類中再次使用,則派生類中的名字就把基類的名字隱藏在其後面了,所有的呼叫都是對派生類成員的呼叫,除非用域說明符::。

上例子吧:

例1:class a

public:

a(){ cout<<"a called"class b : public a

public:

b(){ cout<<"b called"int main(void)

b b;

b.print();

getchar();

return 0;

列印的肯定是"b print",那要想列印"a print"呢,只能改為b.a::print();

需要說明的是這裡並不是對print的過載(根本是同乙個方法嘛,與過載沒關係),應該起名叫「隱藏」比較合適。

例2:class a

public:

a(){ cout<<"a called"class b : public a

public:

b(){ cout<<"b called"int main(void)

b b;

b.print();

getchar();

return 0;

這個例子編譯一下,沒通過!怎麼可能呢,b的print引數不符合,不是基類的print符合要求嗎,怎麼不呼叫呢。原來在派生類b中,print被更改了,這裡是被過載了,和多人把這個叫做「覆蓋」,有點道理,所以a類中的print()怎麼也訪問不到,除非用作用域符號,怎麼改呢,加個引數,加個作用域符號,看你想怎麼用了,在這裡只是想說明「覆蓋」這麼件事。

虛基類 虛擬繼承 虛函式?

前幾天師兄想考我什麼是虛基類的時候,我說是因為多重繼承中,如果出現菱形繼承 multiple inheritance diamond 不想要末端派生類有兩份最底層基類的資料的話,就得用虛擬繼承。結果居然被懟了,說我說的啥玩意兒?我還以為我記錯了,實際上是他把虛基類和含有虛函式的基類給混淆了。今天讀了...

C 虛基類 虛擬繼承

虛基類建構函式的引數必須由最新派生出的類負責初始化,即使不是直接繼承,示例程式的虛基類的建構函式只執行一次 include include include using namespace std class base class base1 virtual public base class bas...

虛基類與虛擬繼承

虛擬繼承 顧名思義就是繼承了但不給開闢空間 class b0 此段 中b0就是虛基類 public int m class b1 virtual public b0 b1虛擬繼承b0 public int n class b2 virtual public b0 b2虛擬繼承b0 public in...