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

歡迎訪問 生活随笔!

生活随笔

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

c/c++

C/C++学习之路: 继承

發布時間:2024/4/11 c/c++ 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C/C++学习之路: 继承 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

C/C++學習之路: 繼承


目錄

  • 繼承概述
  • 派生類訪問控制
  • 繼承中的構造和析構
  • 繼承中同名函數的處理方法
  • 非自動繼承的函數
  • 繼承中靜態成員特性
  • 多繼承

  • 1. 繼承概述

    1. 繼承基本概念

  • c++最重要的特征是代碼重用,通過繼承機制可以利用已有的數據類型來定義新的數據類型,新的類不僅擁有舊類的成員,還擁有新定義的成員。
  • 一個B類繼承于A類,或稱從類A派生類B。這樣的話,類A成為基類(父類), 類B成為派生類(子類)。
  • 派生類中的成員,包含兩大部分:
  • 一類是從基類繼承過來的,一類是自己增加的成員。
  • 從基類繼承過過來的表現其共性,而新增的成員體現了其個性。
  • 2. 派生類定義

  • 派生類定義格式:
  • Class 派生類名 : 繼承方式 基類名{//派生類新增的數據成員和成員函數}
  • 三種繼承方式:
  • public : 公有繼承
  • private : 私有繼承
  • protected : 保護繼承
  • 從繼承源上分:
  • 單繼承:指每個派生類只直接繼承了一個基類的特征
  • 多繼承:指多個基類派生出一個派生類的繼承關系,多繼承的派生類直接繼承了不止一個基類的特征

  • 2. 派生類訪問控制

  • 派生類繼承基類,派生類擁有基類中全部成員變量和成員方法(除了構造和析構之外的成員方法),但是在派生類中,繼承的成員并不一定能直接訪問,不同的繼承方式會導致不同的訪問權限。
  • 派生類的訪問權限規則如下:

  • //基類 class A { public:int mA; protected:int mB; private:int mC; };//1. 公有(public)繼承 class B : public A { public:void PrintB() {cout << mA << endl; //可訪問基類public屬性cout << mB << endl; //可訪問基類protected屬性//cout << mC << endl; //不可訪問基類private屬性} };class SubB : public B {void PrintSubB() {cout << mA << endl; //可訪問基類public屬性cout << mB << endl; //可訪問基類protected屬性//cout << mC << endl; //不可訪問基類private屬性} };void test01() {B b;cout << b.mA << endl; //可訪問基類public屬性//cout << b.mB << endl; //不可訪問基類protected屬性//cout << b.mC << endl; //不可訪問基類private屬性 }//2. 私有(private)繼承 class C : private A { public:void PrintC() {cout << mA << endl; //可訪問基類public屬性cout << mB << endl; //可訪問基類protected屬性//cout << mC << endl; //不可訪問基類private屬性} };class SubC : public C {void PrintSubC() {//cout << mA << endl; //不可訪問基類public屬性//cout << mB << endl; //不可訪問基類protected屬性//cout << mC << endl; //不可訪問基類private屬性} };void test02() {C c;//cout << c.mA << endl; //不可訪問基類public屬性//cout << c.mB << endl; //不可訪問基類protected屬性//cout << c.mC << endl; //不可訪問基類private屬性 }//3. 保護(protected)繼承 class D : protected A { public:void PrintD() {cout << mA << endl; //可訪問基類public屬性cout << mB << endl; //可訪問基類protected屬性//cout << mC << endl; //不可訪問基類private屬性} };class SubD : public D {void PrintD() {cout << mA << endl; //可訪問基類public屬性cout << mB << endl; //可訪問基類protected屬性//cout << mC << endl; //不可訪問基類private屬性} };void test03() {D d;//cout << d.mA << endl; //不可訪問基類public屬性//cout << d.mB << endl; //不可訪問基類protected屬性//cout << d.mC << endl; //不可訪問基類private屬性 }

    3. 繼承中的構造和析構

  • 子類對象在創建時會首先調用父類的構造函數,父類構造函數執行完畢后,才會調用子類的構造函數
  • 當父類構造函數有參數時,需要在子類初始化列表(參數列表)中顯示調用父類構造函數
  • 析構函數調用順序和構造函數相反
  • class A { public:A() {cout << "A類構造函數!" << endl;}~A() {cout << "A類析構函數!" << endl;} };class B : public A { public:B() {cout << "B類構造函數!" << endl;}~B() {cout << "B類析構函數!" << endl;} };class C : public B { public:C() {cout << "C類構造函數!" << endl;}~C() {cout << "C類析構函數!" << endl;} };void test() {C c; }

    4. 繼承中同名函數的處理方法

  • 當子類成員和父類成員同名時,子類依然從父類繼承同名成員
  • 如果子類有成員和父類同名,子類訪問其成員默認訪問子類的成員(本作用域,就近原則)
  • 在子類通過作用域::進行同名成員區分(在派生類中使用基類的同名成員,顯示使用類名限定符)
  • class Base { public:Base() : mParam(0) {}void Print() { cout << mParam << endl; }public:int mParam; };class Derived : public Base { public:Derived() : mParam(10) {}void Print() {//在派生類中使用和基類的同名成員,顯示使用類名限定符cout << Base::mParam << endl;cout << mParam << endl;}//返回基類重名成員int &getBaseParam() { return Base::mParam; }public:int mParam; };int main() {Derived derived;//派生類和基類成員屬性重名,子類訪問成員默認是子類成員cout << derived.mParam << endl; //10derived.Print();//類外如何獲得基類重名成員屬性derived.getBaseParam() = 100;cout << "Base:mParam:" << derived.getBaseParam() << endl;return EXIT_SUCCESS; }
  • 任何時候重新定義基類中的一個重載函數,在新類中所有的其他版本將被自動隱藏(非覆蓋,可通過類作用域運算符調用)
  • class Base{ public://重載函數void func1(){cout << "Base::void func1()" << endl;};void func1(int param){cout << "Base::void func1(int param)" << endl;}//非重載函數void myfunc(){cout << "Base::void myfunc()" << endl;} };class Derived1 : public Base{}; class Derived2 : public Base{ public:void myfunc(){//基類myfunc被隱藏,可通過類作用域運算符指定調用基類myfunc函數//Base::myfunc();cout << "Derived2::void myfunc()" << endl;} }; class Derived3 : public Base{ public://改變成員函數的參數列表void func1(int param1, int param2){//Base::func1(10); //類的內部可通過類作用域運算符訪問基類重載版本的函數cout << "Derived3::void func1(int param1,int param2)" << endl;}; }; class Derived4 : public Base{ public://改變成員函數的返回值int func1(int param){Base::func1(10);cout << "Derived4::int func1(int param)" << endl;return 0;} };//和基類非重載函數重名 void test01(){Derived1 derived1;derived1.myfunc();//和基類函數重名Derived2 derived2;derived2.myfunc(); }//和基類重載函數重名 void test02(){Derived3 derived3;//derived3.func1(); //基類重載版本的函數fun1被全部隱藏,子類外部不可訪問//derived3.func1(10);derived3.func1(10,20);Derived4 derived4;//derived4.func1(); //基類重載版本的函數fun1被全部隱藏,子類外部不可訪問derived4.func1(10); }

    5. 非自動繼承的函數

  • 不是所有的函數都能自動從基類繼承到派生類中。
  • 構造函數和析構函數用來處理對象的創建和析構操作,構造函數和析構函數不能被繼承,必須為每一個特定的派生類分別創建。
  • 另外operator=也不能被繼承,因為它完成類似構造函數的行為。
  • 盡管我們知道如何由=右邊的對象如何初始化=左邊的對象的所有成員,但是這個并不意味著對其派生類依然有效。
  • 在繼承的過程中,如果沒有創建這些函數,編譯器會自動生成它們。

  • 6. 繼承中靜態成員特性

  • 靜態成員函數和非靜態成員函數的共同點:
  • 他們都可以被繼承到派生類中。
  • 如果重新定義一個靜態成員函數,所有在基類中的其他重載函數會被隱藏。
  • 如果我們改變基類中一個函數的特征,所有使用該函數名的基類版本都會被隱藏。
  • 靜態成員函數不能是虛函數(virtual function)
  • class Base { public:static int getNum() { return sNum; }static int getNum(int param) {return sNum + param;}public:static int sNum; };int Base::sNum = 10;class Derived : public Base { public:static int sNum; //基類靜態成員屬性將被隱藏 #if 0//重定義一個函數,基類中重載的函數被隱藏static int getNum(int param1, int param2){return sNum + param1 + param2;} #else//改變基類函數的某個特征,返回值或者參數個數,將會隱藏基類重載的函數static void getNum(int param1, int param2) {cout << sNum + param1 + param2 << endl;}#endif };int Derived::sNum = 20;

    7. 多繼承

    1. 多繼承基本概念

  • 我們可以從一個類繼承,我們也可以能同時從多個類繼承,這就是多繼承。
  • 但是從多個類繼承可能會導致函數、變量等同名導致較多的歧義。
  • 解決方法就是顯示指定調用那個基類的版本。
  • class Base1 { public:void func1() { cout << "Base1::func1" << endl; } };class Base2 { public:void func1() { cout << "Base2::func1" << endl; }void func2() { cout << "Base2::func2" << endl; } };//派生類繼承Base1、Base2 class Derived : public Base1, public Base2 { };int main() {Derived derived;//func1是從Base1繼承來的還是從Base2繼承來的?//derived.func1();derived.func2();//解決歧義:顯示指定調用那個基類的func1derived.Base1::func1();derived.Base2::func1();return EXIT_SUCCESS; }

    2. 菱形繼承和虛繼承

  • 兩個派生類繼承同一個基類而又有某個類同時繼承者兩個派生類,這種繼承被稱為菱形繼承,或者鉆石型繼承。
  • 這種繼承所帶來的問題:
  • 羊繼承了動物的數據和函數,駝同樣繼承了動物的數據和函數,當草泥馬調用函數或者數據時,就會產生二義性。
  • 草泥馬繼承自動物的函數和數據繼承了兩份,這份數據我們只需要一份就可以。
  • class BigBase { public:BigBase() { mParam = 0; }void func() { cout << "BigBase::func" << endl; }public:int mParam; };class Base1 : public BigBase { };class Base2 : public BigBase { };class Derived : public Base1, public Base2 { };int main() {Derived derived;//1. 對“func”的訪問不明確//derived.func();//cout << derived.mParam << endl;cout << "derived.Base1::mParam:" << derived.Base1::mParam << endl;cout << "derived.Base2::mParam:" << derived.Base2::mParam << endl;//2. 重復繼承cout << "Derived size:" << sizeof(Derived) << endl; //8return EXIT_SUCCESS; }
  • 對于這種菱形繼承所帶來的兩個問題,c++為我們提供了一種方式,采用虛基類。
  • class BigBase { public:BigBase() { mParam = 0; }void func() { cout << "BigBase::func" << endl; }public:int mParam; };class Base1 : virtual public BigBase { };class Base2 : virtual public BigBase { };class Derived : public Base1, public Base2 { };int main() {Derived derived;//二義性問題解決derived.func();cout << derived.mParam << endl;//輸出結果:12cout << "Derived size:" << sizeof(Derived) << endl;return EXIT_SUCCESS; }
  • 以上程序Base1 ,Base2采用虛繼承方式繼承BigBase,那么BigBase被稱為虛基類。
  • 通過虛繼承解決了菱形繼承所帶來的二義性問題。
  • 3. 虛繼承實現原理

  • BigBase 菱形最頂層的類,內存布局圖沒有發生改變。
  • Base1和Base2通過虛繼承的方式派生自BigBase,這兩個對象的布局圖中可以看出編譯器為我們的對象中增加了一個vbptr (virtual base pointer),vbptr指向了一張表,這張表保存了當前的虛指針相對于虛基類的首地址的偏移量。
  • Derived派生于Base1和Base2,繼承了兩個基類的vbptr指針,并調整了vbptr與虛基類的首地址的偏移量。
  • 使得這種菱形問題在繼承時候能只繼承一份數據,并且也解決了二義性的問題。
  • 當使用虛繼承時,虛基類是被共享的,也就是在繼承體系中無論被繼承多少次,對象內存模型中均只會出現一個虛基類的子對象(這和多繼承是完全不同的)。
  • 即使共享虛基類,但是必須要有一個類來完成基類的初始化(因為所有的對象都必須被初始化,哪怕是默認的),同時還不能夠重復進行初始化
  • C++標準中選擇在每一次繼承子類中都必須書寫初始化語句(因為每一次繼承子類可能都會用來定義對象),但是虛基類的初始化是由最后的子類完成,其他的初始化語句都不會調用。
  • 總結

    以上是生活随笔為你收集整理的C/C++学习之路: 继承的全部內容,希望文章能夠幫你解決所遇到的問題。

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