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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

第三章-继承与派生

發布時間:2025/3/21 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第三章-继承与派生 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第三章-繼承與派生

文章目錄

  • 第三章-繼承與派生
    • 1.類的繼承
      • 基類與派生類繼承規則(訪問權限)
    • 2.類型兼容規則
    • 3.派生類構造函數
    • 4.派生類析構函數
    • 5.同名隱藏
    • 6.虛基類
    • Tips

1.類的繼承

類的繼承,是新的類從已有類那里得到已有的特性;從已有類產生新類的過程就是類的派生

  • 原有的類叫做基類或者父類,產生的新類叫做派生類或者子類
  • 一個派生類可以有多個基類,即多繼承,只有一個基類的叫做單繼承
  • 如果不顯式地給出繼承方式,默認為私有繼承
派生類的語法定義: class 派生類名:繼承方式 基類名1,繼承方式 基類名2,······,繼承方式 基類名n {派生類成員聲明; };
  • 派生類包含了它的全部基類中除了構造函數和析構函數之外的所有成員
  • 派生類成員是指除了從基類繼承來的所有成員之外,新增加的數據和函數成員

示例:

//基類 class A { public:int a;void printA(){printf("A");} }; //派生類,公有繼承 class B:public A { public: int b;void printB(){printf("B");} };

基類與派生類繼承規則(訪問權限)

派生類訪問權限公有類型保護類型私有類型不可訪問類型
公有繼承公有保護不可訪問不可訪問
保護繼承保護保護不可訪問不可訪問
私有繼承私有私有不可訪問不可訪問

2.類型兼容規則

類型兼容規則是指需要基類對象的地方,都可以使用公有派生類的對象來替代

  • 派生類的對象可以隱含地轉化為基類對象
  • 派生類的對象可以初始化基類的引用
  • 派生類的指針可以隱含地轉化為基類指針
  • 在替代之后,派生類對象可以作為基類對象使用,但只能使用從基類繼承的成員

    class B{···} class D:public B{···} B b1,*pb1; D d1; (1)b1=d1; //派生類對象轉化為基類對象 (2)B &rb=d1; //d1為rb的一個引用,修改它成員變量的值,rb里面也會改變 (3)pb1=&d1; //派生類對象的地址也可以轉化為基類指針,即基類指針指向派生類對象 class A{ public:void display(){cout<<"A::display()"<<endl;} }; class B:public A{ public:void display(){cout<<"B::display()"<<endl;} }; class C:public B{ public:void display(){cout<<"C::display()"<<endl;} }; void fun(A* ptr){ptr->display(); } int main() {A a;B b;C c;fun(&a); //用 A對象的指針調用fun函數fun(&b);fun(&c); } /* 輸出結果均為 A::display() */

    3.派生類構造函數

    構造派生類的對象時,就要對基類的成員對象和新增成員對象進行初始化

    派生類構造函數的一般語法形式為:

    派生類名 :: 派生類名(參數表):基類名n(參數表),成員對象名n(參數表) {其他初始化操作; }
    • 如果對基類初始化時,需要調用基類的帶有形參表的構造函數時,派生類就必須聲明構造函數,提供一個將參數傳遞給基類構造函數的途徑
    • 如果不需要調用基類的帶參數的構造函數,也不需要調用新增的成員變量的帶參數的構造函數,派生類也可以不聲明構造函數,全部采用默認的構造函數

    派生類構造函數執行的一般次序如下:

  • 調用基類構造函數,調用順序按照它們被繼承時聲明的順序(從左到右)
  • 對派生類新增的成員對象初始化,調用順序按照它們在類中聲明的順序
  • 執行派生類的構造函數體中的內容
  • class A{ public:A(int i){cout<<"A "<<i<<endl;} }; class B{ public:B(int j){cout<<"B "<<j<<endl;} }; class C{ public:C(){cout<<"C *"<<endl;} }; class D:public B,public A,public C{ public:D(int x,int y,int z,int w):A(x),mem2(w),mem1(z),B(y){} private:A mem1;B mem2;C mem3; }; int main() {D obj(1,2,3,4);return 0; } /* 程序的運行結果為:B 2A 1C *A 3B 4C * */

    如果為派生類編寫復制構造函數,一般需要為基類相應的復制構造函數傳遞參數(這里可以使用替代兼容規則)

    class A{A(A & n){} }; class B:public A{B(B & m):A(m){ //用派生類對象初始化基類的引用} };

    4.派生類析構函數

    派生類中編寫析構函數,只需要在函數體中把派生類新增的非對象成員清理工作做好就行了,系統會自己調用基類及對象成員的析構函數來對基類及對象成員進行清理。

    • 執行次序與構造函數完全相反,首先執行析構函數的函數體,然后對派生類新增的類型的成員對象進行清理,最后對所有從基類繼承而來的成員清理。
    • 沒有顯式聲明析構函數的話,編譯系統會自動生成默認的析構函數
    class A{ public:A(int i){cout<<"A "<<i<<endl;}~A(){cout<<"A is destructed"<<endl;} }; class B{ public:B(int j){cout<<"B "<<j<<endl;}~B(){cout<<"B is destructed"<<endl;} }; class C{ public:C(){cout<<"C *"<<endl;}~C(){cout<<"C is destructed"<<endl;} }; class D:public B,public A,public C{ public:D(int x,int y,int z,int w):A(x),mem2(w),mem1(z),B(y){} private:A mem1;B mem2;C mem3; }; int main() {D obj(1,2,3,4);return 0; } /* 程序的運行結果為:B 2A 1C *A 3B 4C *C is destructedB is destructedA is destructedC is destructedA is destructedB is destructed */

    5.同名隱藏

    如果派生類中聲明了一個和基類成員同名的新成員,派生類的新成員就隱藏了外層同名成員,這叫做同名隱藏

    這時使用“對象名 . 成員名”只能訪問到派生類新增成員,如果想要調用基類的成員,必須用基類名和作用域符進行限定

    class A{A(){x=12;}int x; }; class B:public A{B(){x=23;}void print(){cout<< A::x <<endl;cout<< x <<endl; //B類中的x}int x; }; int main(){B obj;cout<< obj.A::x <<endl;cout<< obj.B::x <<endl;//為了避免二義性,必須用基類名和作用域符 }

    為了不產生二義性,也可以使用using關鍵字加以澄清

    將using用于基類中的函數名,派生類中定義同名但參數不同的函數,基類的函數就不會被隱藏,兩個重載的函數將會并存在于派生類作用域中

    class A{ public:void fun(){···} }; class B:public A{ public:using A :: fun;void fun(int i){···} }; //使用 B的對象,既可以直接調用無參fun函數,也可以直接調用有參fun函數

    6.虛基類

    如果某個派生類的部分或全部直接基類是從另一個共同的基類派生而來,在這些直接基類中,從上一級基類繼承來的成員就擁有相同的名稱,因此派生類中也就會產生同名現象,對這種類型的同名成員也要用作用域符來唯一標識,而且必須用直接基類進行限定

    class A{int x; }; class A1:public A{···}; class A2:public A{···}; class B:public A1,public A2{···}; int main(){B b;cout<< b.A1::x <<endl; }

    可以將共同基類設置為虛基類,這樣從不同路徑繼承而來的同名數據成員在內存中只有一個副本,同一函數名也只有一個映射

    虛基類的聲明是在派生類定義過程中進行的,語法為:

    class 派生類名:virtual 繼承方式 基類名

    在多繼承情況下,虛基類關鍵字的作用范圍和繼承方式關鍵字相同,只對其后的基類起作用。聲明了虛基類之后,虛基類的成員在進一步的派生過程中和派生類一起維護同一個內存數據副本

    class A{ public:A(){x=10;}int x; }; class B1:virtual public A{ public:void fun1(){x=23;} }; class B2:virtual public A{ public:void fun2(){x=45;} }; class C:public B1,public B2{ public:void fun3(){x=67;} }; int main() {C c;cout<<c.A::x<<","<<c.x<<endl;c.fun1();cout<<c.A::x<<","<<c.x<<endl;c.fun2();cout<<c.A::x<<","<<c.x<<endl;c.fun3();cout<<c.A::x<<","<<c.x<<endl;return 0; } /* 程序的運行結果為:10,1023,2345,4567,67 */
    • 如果虛基類沒有聲明構造函數,那么所有相關類使用的都是默認構造函數
    • 如果虛基類聲明有帶形參的構造函數,并且沒有聲明默認模式的構造函數,那么在整個繼承關系中,直接或間接繼承虛基類的所有派生類,都必須在構造函數的成員初始化列表中列出對虛基類的初始化

    建立對象時所指定的類稱為最遠派生類。

    在上一個例子中,對于虛基類A而言,C為最遠派生類

    建立一個對象時,如果對象中含有從虛基類繼承來的成員,虛基類的成員由最遠派生類的構造函數通過調用虛基類的構造函數進行初始化。
    并且只有最遠派生類的構造函數會調用虛基類的構造函數,該派生類的其他基類(如B1、B2)對虛基類構造函數的調用會自動忽略。


    Tips

    構造一個類的對象的一般順序:

  • 如果該類有直接或者間接的虛基類,則先執行虛基類的構造函數。
  • 如果該類有其他基類,則按照它們在繼承聲明的列表中出現的次序,分別執行它們的構造函數,但構造過程中,不在執行它們基類的構造函數。
  • 按照在類定義中出現的順序,對派生類中新增的成員對象進行初始化。對于類類型的成員對象,如果出現在構造函數初始化列表中,則以其中指定的參數執行構造函數。如果未出現,則執行默認構造函數;對于基本數據類型的成員對象,如果出現在構造函數的初始化列表中,則使用其中指定的值為其賦初值,否則什么也不做。
  • 執行構造函數的函數體中的內容。
  • 總結

    以上是生活随笔為你收集整理的第三章-继承与派生的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。