日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++虚基类详解

發(fā)布時間:2025/6/15 c/c++ 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++虚基类详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
我們知道,如果一個派生類有多個直接基類,而這些直接基類又有一個共同的基類,則在最終的派生類中會保留該間接共同基類數(shù)據(jù)成員的多份同名成員。在引用這些同名的成員時,必須在派生類對象名后增加直接基類名,以避免產(chǎn)生二義性,使其惟一地標識一個成員,如:
? ? c1.A::display( )

在一個類中保留間接共同基類的多份同名成員,雖然有時是有必要的,可以在不同的數(shù)據(jù)成員中分別存放不同的數(shù)據(jù),也可以通過構(gòu)造函數(shù)分別對它們進行初始化。但在大多數(shù)情況下,這種現(xiàn)象是人們不希望出現(xiàn)的。因為保留多份數(shù)據(jù)成員的拷貝,不僅占用較多的存儲空間,還增加了訪問這些成員時的困難,容易出錯。而且在實際上,并不需要有多份拷貝。

C++提供虛基類(virtual base class)的方法,使得在繼承間接共同基類時只保留一份成員。假設(shè)類D是類B和類C公用派生類,而類B和類C又是類A的派生類,如圖11.21所示。 設(shè)類A有數(shù)據(jù)成員data和成員函數(shù)fun;派生類B和C分別從類A繼承了data和fun,此外類B還增加了自己的數(shù)據(jù)成員data_b,類C增加了數(shù)據(jù)成員data_c。如果不用虛基類,就會在類D中保留了類A成員data的兩份拷貝,分別表示為int B::data和int C::data。同樣有兩個同名的成員函數(shù),表示為void B::fun()和void C::fun()。類B中增加的成員data_b和類C中增加的成員dat_c不同名,不必用類名限定。此外,類D還增加了自己的數(shù)據(jù)成員data_d和成員函數(shù)fun_d。


圖 11.21
現(xiàn)在,將類A聲明為虛基類,方法如下: 復制純文本新窗口
  • class A //聲明基類A
  • {
  • // 代碼
  • };
  • class B: virtual public A //聲明類B是類A的公用派生類,A是B的虛基類
  • {
  • // 代碼
  • };
  • class C: virtual public A //聲明類C是類A的公用派生類,A是C的虛基類
  • {
  • // 代碼
  • };
  • class A //聲明基類A {// 代碼 }; class B: virtual public A //聲明類B是類A的公用派生類,A是B的虛基類 {// 代碼 }; class C: virtual public A //聲明類C是類A的公用派生類,A是C的虛基類 {// 代碼 }; 注意: 虛基類并不是在聲明基類時聲明的,而是在聲明派生類時,指定繼承方式時聲明的。因為一個基類可以在生成一個派生類時作為虛基類,而在生成另一個派生類時不作為虛基類。

    聲明虛基類的一般形式為:
    ? ?class 派生類名: virtual 繼承方式 ?基類名
    即在聲明派生類時,將關(guān)鍵字 virtual 加到相應的繼承方式前面,經(jīng)過這樣的聲明后,當基類通過多條派生路徑被一個派生類繼承時,該派生類只繼承該基類一次,也就是說,基類成員只保留一次。

    需要注意:為了保證虛基類在派生類中只繼承一次,應當在該基類的所有直接派生類中聲明為虛基類。否則仍然會出現(xiàn)對基類的多次繼承。

    如果在派生類B和C中將類A聲明為虛基類,而在派生類D中沒有將類A聲明為虛基類,則在派生類E中,雖然從類B和C路徑派生的部分只保留一份基類成員,但從類D路徑派生的部分還保留一份基類成員。

    虛基類的初始化

    如果在虛基類中定義了帶參數(shù)的構(gòu)造函數(shù),而且沒有定義默認構(gòu)造函數(shù),則在其所有派生類(包括直接派生或間接派生的派生類)中,通過構(gòu)造函數(shù)的初始化表對虛基類進行初始化。例如 復制純文本新窗口
  • class A //定義基類A
  • {
  • A(int i){ } //基類構(gòu)造函數(shù),有一個參數(shù)};
  • class B :virtual public A //A作為B的虛基類
  • {
  • B(int n):A(n){ } //B類構(gòu)造函數(shù),在初始化表中對虛基類初始化
  • };
  • class C :virtual public A //A作為C的虛基類
  • {
  • C(int n):A(n){ } //C類構(gòu)造函數(shù),在初始化表中對虛基類初始化
  • };
  • class D :public B,public C //類D的構(gòu)造函數(shù),在初始化表中對所有基類初始化
  • {
  • D(int n):A(n),B(n),C(n){ }
  • };
  • class A //定義基類A {A(int i){ } //基類構(gòu)造函數(shù),有一個參數(shù)}; class B :virtual public A //A作為B的虛基類 {B(int n):A(n){ } //B類構(gòu)造函數(shù),在初始化表中對虛基類初始化 }; class C :virtual public A //A作為C的虛基類 {C(int n):A(n){ } //C類構(gòu)造函數(shù),在初始化表中對虛基類初始化 }; class D :public B,public C //類D的構(gòu)造函數(shù),在初始化表中對所有基類初始化 {D(int n):A(n),B(n),C(n){ } }; 注意: 在定義類D的構(gòu)造函數(shù)時,與以往使用的方法有所不同。以往,在派生類的構(gòu)造函數(shù)中只需負責對其直接基類初始化,再由其直接基類負責對間接基類初始化。現(xiàn)在,由于虛基類在派生類中只有一份數(shù)據(jù)成員,所以這份數(shù)據(jù)成員的初始化必須由派生類直接給出。如果不由最后的派生類直接對虛基類初始化,而由虛基類的直接派生類(如類B和類C)對虛基類初始化,就有可能由于在類B和類C的構(gòu)造函數(shù)中對虛基類給出不同的初始化參數(shù)而產(chǎn)生矛盾。所以規(guī)定:在最后的派生類中不僅要負責對其直接基類進行初始化,還要負責對虛基類初始化。

    有的讀者會提出:類D的構(gòu)造函數(shù)通過初始化表調(diào)了虛基類的構(gòu)造函數(shù)A,而類B和類C的構(gòu)造函數(shù)也通過初始化表調(diào)用了虛基類的構(gòu)造函數(shù)A,這樣虛基類的構(gòu)造函數(shù)豈非被調(diào)用了3次?大家不必過慮,C++編譯系統(tǒng)只執(zhí)行最后的派生類對虛基類的構(gòu)造函數(shù)的調(diào)用,而忽略虛基類的其他派生類(如類B和類C) 對虛基類的構(gòu)造函數(shù)的調(diào)用,這就保證了虛基類的數(shù)據(jù)成員不會被多次初始化。

    虛基類的簡單應用舉例

    [例11.9] 在例11. 8(具體代碼請查看:C++類的多重繼承)的基礎(chǔ)上,在Teacher類和Student類之上增加一個共同的基類Person。作為人員的一些基本數(shù)據(jù)都放在Person中,在 Teacher類和Student類中再增加一些必要的數(shù)據(jù)。可寫出以下程序: 復制純文本新窗口
  • #include <iostream>
  • #include <string>
  • using namespace std;
  • //聲明公共基類Person
  • class Person
  • {
  • public:
  • Person(string nam,char s,int a) //構(gòu)造函數(shù)
  • {
  • name=nam;
  • sex=s;
  • age=a;
  • }
  • protected: //保護成員
  • string name;
  • char sex;
  • int age;
  • };
  • ?
  • //聲明Person的直接派生類Teacher
  • class Teacher:virtual public Person //聲明Person為公用繼承的虛基類
  • {
  • public:
  • Teacher(string nam,char s,int a, string t):Person(nam,s,a)//構(gòu)造函數(shù)
  • {
  • title=t;
  • }
  • protected: //保護成員
  • string title; //職稱
  • };
  • ?
  • //聲明Person的直接派生類Student
  • class Student:virtual public Person //聲明Person為公用繼承的虛基類
  • {
  • public:
  • Student(string nam,char s,int a,float sco) //構(gòu)造函數(shù)
  • :Person(nam,s,a),score(sco){ } //初始化表
  • protected: //保護成員
  • float score; //成績
  • };
  • ?
  • //聲明多重繼承的派生類Graduate
  • class Graduate:public Teacher,public Student //Teacher和Student為直接基類
  • {
  • public:
  • Graduate(string nam,char s,int a, string t,float sco,float w)//構(gòu)造函數(shù)
  • :Person(nam,s,a),Teacher(nam,s,a,t),Student(nam,s,a,sco),wage(w){}
  • //初始化表
  • void show( ) //輸出研究生的有關(guān)數(shù)據(jù)
  • {
  • cout<<"name:"<<name<<endl;
  • cout<<"age:"<<age<<endl;
  • cout<<"sex:"<<sex<<endl;
  • cout<<"score:"<<score<<endl;
  • cout<<"title:"<<title<<endl;
  • cout<<"wages:"<<wage<<endl;
  • }
  • private:
  • float wage; //工資
  • };
  • ?
  • //主函數(shù)
  • int main( )
  • {
  • Graduate grad1("Wang-li",'f',24,"assistant",89.5,1234.5);
  • grad1.show( );
  • return 0;
  • }
  • #include <iostream> #include <string> using namespace std; //聲明公共基類Person class Person { public:Person(string nam,char s,int a) //構(gòu)造函數(shù){name=nam;sex=s;age=a;} protected: //保護成員string name;char sex;int age; };//聲明Person的直接派生類Teacher class Teacher:virtual public Person //聲明Person為公用繼承的虛基類 { public: Teacher(string nam,char s,int a, string t):Person(nam,s,a)//構(gòu)造函數(shù){title=t;} protected: //保護成員string title; //職稱 };//聲明Person的直接派生類Student class Student:virtual public Person //聲明Person為公用繼承的虛基類 { public:Student(string nam,char s,int a,float sco) //構(gòu)造函數(shù):Person(nam,s,a),score(sco){ } //初始化表 protected: //保護成員float score; //成績 };//聲明多重繼承的派生類Graduate class Graduate:public Teacher,public Student //Teacher和Student為直接基類 { public:Graduate(string nam,char s,int a, string t,float sco,float w)//構(gòu)造函數(shù):Person(nam,s,a),Teacher(nam,s,a,t),Student(nam,s,a,sco),wage(w){}//初始化表void show( ) //輸出研究生的有關(guān)數(shù)據(jù){cout<<"name:"<<name<<endl;cout<<"age:"<<age<<endl;cout<<"sex:"<<sex<<endl;cout<<"score:"<<score<<endl;cout<<"title:"<<title<<endl;cout<<"wages:"<<wage<<endl;} private:float wage; //工資 };//主函數(shù) int main( ) {Graduate grad1("Wang-li",'f',24,"assistant",89.5,1234.5);grad1.show( );return 0; }
    對程序的兩點說明:
    1) 請注意各類的構(gòu)造函數(shù)的寫法。在Person類中定義了包含3個形參的構(gòu)造函數(shù),用它對數(shù)據(jù)成員name、sex和age進行初始化。在Teacher和Student類的構(gòu)造函數(shù)中,按規(guī)定要在初始化表中包含對基類的初始化,盡管對虛基類來說,編譯系統(tǒng)不會由此調(diào)用基類的構(gòu)造函數(shù),但仍然應當按照派生類構(gòu)造函數(shù)的統(tǒng)一格式書寫。在最后派生類Graduate的構(gòu)造函數(shù)中,既包括對虛基類構(gòu)造函數(shù)的調(diào)用,也包括對其直接基類的初 始化。

    2) 在Graduate類中,只保留一份基類的成員,因此可以用Graduate類中的show函數(shù)引用Graduate類對象中的公共基類Person的數(shù)據(jù)成員name、sex、age的值,不需要加基類名和域運算符(::),不會產(chǎn)生二義性。

    可以看到:使用多重繼承時要十分小心,經(jīng)常會出現(xiàn)二義性問題。前面介紹的例子是簡單的,如果派生的層次再多一些,多重繼承更復雜一些,程序設(shè)計人員很容易陷人迷 魂陣,程序的編寫、調(diào)試和維護工作都會變得更加困難。因此,許多專業(yè)人員認為:不要提倡在程序中使用多重繼承,只有在比較簡單和不易出現(xiàn)二義性的情況或?qū)嵲诒匾獣r才使用多重繼承,能用單一繼承解決的問題就不要使用多重繼承。也是由于這個原因,有些面向?qū)ο蟮某绦蛟O(shè)計語言(如Java,Smalltalk)并不支持多重繼承。

    總結(jié)

    以上是生活随笔為你收集整理的C++虚基类详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。