C++基礎(chǔ)3:繼承

1. 語法

  • 原則:is-a
    父類/子類
    基類/派生類

  • 語法

class 派生類 : [訪問限定符] 基類 {
  成員
}

如果不寫繼承訪問限定符,默認(rèn)是private

實(shí)例:圖形繼承

No. 英文 中文
1 Triangle 三角形
2 Equilateral Triangle 等邊三角形
3 Isosceles Triangle 等腰三角形
4 Right-angled Triangle 直角三角形

2. 成員的訪問權(quán)限

public protected private
類成員函數(shù)
友元函數(shù)
子類函數(shù) ×
類對象 × ×

子類繼承了父類所有的成元變量和成員函數(shù)。與訪問限定符無關(guān)。訪問限定符只是限制了訪問。
子類訪問父類成員變量,把父類成員變量訪問限制符,改為protected。

  • 繼承訪問權(quán)限變化

分為子類內(nèi)部和子類對象兩種訪問方式。

子類內(nèi)部訪問public繼承的父類成員變量

class Base {
public:
    int public_data;
protected:
    int protected_data;
private:
    int private_data;
};
class Derive:public Base {
public:
    void test() {
        cout<< public_data <<endl;
        cout<< protected_data <<endl;
        cout<< private_data <<endl;
    }
};

子類內(nèi)部訪問public繼承的父類成員函數(shù)

#include <iostream>

using std::cout;
using std::endl;

class Base {
public:
    void public_func(){};
protected:
    void protected_func(){};
private:
    void private_func(){};
};
class Derive:public Base {
public:
    void test() {
        public_func();
        protected_func();
        private_func();
    }
};
  • 子類內(nèi)部訪問父類成員
public protected private
public 繼承 ×
protected 繼承 ×
private 繼承 ×

子類內(nèi)部訪問父類成員,只能訪問publicprotected成員。

  • 子類對象訪問父類成員
public protected private
public 繼承 × ×
protected 繼承 × × ×
private 繼承 × × ×

子類只有public繼承父類的時候,才能訪問父類的public成員,其他都不能訪問。
通常子類使用public繼承父類。

子類對象訪問父類成員訪問限定符的變化

繼承方式\父類成員 public protected private
public 繼承 public protected 不可見
protected 繼承 protected protected 不可見
private 繼承 private private 不可見
public繼承
protected繼承
private繼承

小結(jié)

  • public無論類內(nèi)部還是類對象都可以訪問。
  • protected類對象不可訪問,類內(nèi)部與繼承類的內(nèi)部可以訪問。
  • private只有類內(nèi)部可以訪問。

3. 繼承關(guān)系的構(gòu)造順序

  • 派生類的構(gòu)造函數(shù)與析構(gòu)函數(shù)的調(diào)用順序
  • 派生類的構(gòu)造函數(shù)調(diào)用順序
    子對象構(gòu)造、成員變量構(gòu)造、父對象構(gòu)造的順序
  • 派生類的析構(gòu)函數(shù)調(diào)用順序
    子對象析構(gòu)、成員變量析構(gòu)、父對象析構(gòu)的順序
#include <iostream>
using std::cout;
using std::endl;
class Member{
public:
    Member(){
        cout << "Member Init" <<endl;
    }
    ~Member(){
        cout << "Member Destroy" <<endl;
    }
};
class Parent{
public:
    Parent(){
        cout << "Parent Init" <<endl;
    }
    ~Parent(){
        cout << "Parent Destory" <<endl;
    }
};
class Son : public Parent{
public:
    Son(){
        cout << "Son Init" <<endl;
    }
    ~Son(){
        cout << "Son Destroy" <<endl;
    }
private:
    Member m;
};
int main(){
    Son son;
}

沒有默認(rèn)構(gòu)造函數(shù)的基類在派生類的初始化,必須在初始化列表中初始化。

  • 同名隱藏規(guī)則
    概念:子類的成員函數(shù)與基類成員函數(shù)同名,子類的函數(shù)將會隱藏基類的所有同名函數(shù)。
#include <iostream>
using std::cout;
using std::endl;
class Base { 
public:
  void show(int data){
    cout<<"Base::show("<<data<<")"<<endl;
  }
};
class Derive:public Base {
public:
  void show(){
    cout<<"Derive::show()"<<endl;
  }
};
int main() {
  Derive derive;
  derive.show(); 
  derive.show(123); 
}

修改成指針方式,試一下。

int main() {
      Derive* pderive = new Derive;
      pderive->show();
      pderive->show(123); 
      delete pderive;
}

解決方法:

  1. Derived類對象調(diào)用被隱藏的父類函數(shù)時,在函數(shù)前面加上父類限定符。例如:
  • derive.show(123)derive.Base::show(123)
  • pderive->show(123)pderive->Base::show(123)
  1. Derived類中的名稱會隱藏Base類中同名的名稱,在public繼承中我們可以通過引入using聲明。
class Derive:public Base {
public:
  using Base::show;
  void show(){
    cout<<"Derive::show()"<<endl;
  }
};

隱藏背后原因是為防止在程序庫或應(yīng)用框架內(nèi)建立新的derived class時從疏遠(yuǎn)的base classes繼承重載函數(shù)。
--- Effetive C++

函數(shù)同名的情況總結(jié)

名稱 英語
重載 overload
重寫(覆蓋) override
隱藏 hide

  • 賦值兼容規(guī)則
    概念:在任何需要基類對象的地方都可以使用公有的派生類對象來代替。反之,不可。

三種情況

  1. 派生類的對象可以賦值給基類對象。
Base base;
Derive derive;
base = derive;

對象切割(Object Slicing):在賦值時舍棄派生類自己的成員,只進(jìn)行基類數(shù)據(jù)成員的賦值。

問題:

  1. 子類特有的成員變量能否被父類訪問?
  2. 此時父類對象(base)的大小與子類對象(derive)大小是否一致?
  3. 子類對象是否可以初始化父類對象?
    設(shè)計一段代碼驗(yàn)證
  1. 派生類的對象可以初始化基類的引用。
Derive derive;
Base& base = derive;

驗(yàn)證上述問題。

  1. 派生類對象的地址可以賦給指向基類的指針。
    指向基類對象的指針變量也可以指向派生類對象。
Derive derive;
Base* base = &derive;

驗(yàn)證上述問題。

  • 向上轉(zhuǎn)換:派生類對象賦值給基類
  • 向下轉(zhuǎn)換:基類對象賦值給派生類

練習(xí)

  • 派生類的對象可以賦值給基類的對象
int main() {
      Base base;
      Derive derive;
      base = derive;
      base.show(123);
      //base.show();
      //base.Derive::show();
}
  • 派生類對象的地址賦給基類的指針變量
int main() {
      Base* pbase = new Derive;
      pbase->show(123);
      //pbase->show();
      //pbase->Derive::show();
}

指針訪問派生類中由基類繼承來的對象,不能訪問派生類中的新成員

  • 派生類對象可以初始化基類的引用
int main() {
      Derive derive;
      Base& base = derive;
      base.show(123);
      //base.show();
      //base.Derive::show();
}

引用訪問派生類中由基類繼承來的對象,不能訪問派生類中的新成員


多重繼承

一個類可以同時繼承多個父類的行為和特征功能。
逗號分割的基類列表

class 類名 : public 基類1,public 基類2{
};

案例:等腰直角三角形繼承等腰三角形與直角三角形

多重繼承基類構(gòu)造順序?

鉆石繼承/菱形繼承

  • 概念:兩個子類繼承同一個父類,而又有子類同時繼承這兩個子類。

  • 問題:下面B、C繼承與A,D同時繼承BC,那么同時D的實(shí)例調(diào)用A的成員函數(shù)會有什么情況?

#include <iostream>
using std::cout;
using std::endl;
class A{
public:
      void test();
private:
      int id;
};
void A::test(){
    cout << __func__ << endl;
}
class B : public A {};
class C : public A {};
class D : public B,public C{};
int main(){
    cout << "A size:" << sizeof(A) << endl;
    cout << "B size:" << sizeof(B) << endl;
    cout << "C size:" << sizeof(C) << endl;
    cout << "D size:" << sizeof(D) << endl;
    D d;
    d.test();
}
  • 原因:BC都繼承了A的成員函數(shù)test(),D同時繼承了BC,調(diào)用test()無法確定是B還是C的。
  • 解決:給調(diào)用的成員函數(shù)前加上訪問限定符,明確指定調(diào)用成員函數(shù)所屬的類d.B::test();或者d.C::test();。
  • 擴(kuò)展: 嘗試d.A::test(),會有什么結(jié)果?

注意: DsizeofBC的和,也就是兩個A。
問題:不同途徑繼承來的同名的成員在內(nèi)存中有不同的拷貝,造成數(shù)據(jù)不一致。

  • 解決:虛繼承&虛基類
  • 虛繼承&虛基類
    虛繼承:在繼承定義中包含了virtual關(guān)鍵字的繼承關(guān)系。
    虛基類:在虛繼承體系中的通過virtual繼承而來的基類。
class 類名:public virtual 基類{
}

虛基類是一個相對概念,在虛繼承關(guān)系中,父類相對與子類是虛基類。

虛基類與普通基類的構(gòu)造順序?

擴(kuò)展:UML

派生類的編碼

  1. 構(gòu)造與析構(gòu)
  2. 繼承基類成員
  3. 重載基類成員函數(shù)
  4. 增加新成員

測驗(yàn)

  • 寫出下列程序的執(zhí)行結(jié)果,并分析結(jié)果。(如果程序編譯有錯,請分析原因,并寫出解決方法)
#include <iostream>

using namespace std;

class Base{
    public:
    Base(){
        cout << "Base constuct" << endl;
    }
    ~Base(){
        cout << "Base destuct" << endl;
    }
};
class Member{
public:
    Member(){
        cout << "Member constuct" << endl;
    }
    ~Member(){
        cout << "Member destuct" << endl;
    }
};
class Derive:public Base{
public:
    Derive(){
        cout << "Derive constuct" << endl;
    }
    ~Derive(){
        cout << "Derive destuct" << endl;
    }
private:
    Member m;
};
int main(){
    Derive d;
}

關(guān)于多重繼承

  1. 什么是多重繼承?同時繼承多個父類。
  2. 多重繼承有什么危害?菱形繼承/鉆石繼承。
  3. 什么是菱形繼承/鉆石繼承?多重繼承的兩個或多個父類具有相同的祖先類。
  4. 菱形繼承/鉆石繼承有什么危害?因?yàn)槎嘀乩^承的兩個或多個父類具有相同的祖先類。所以會有完全相同的屬性和方法。因此當(dāng)前多重繼承類有兩份相同的屬性和方法。使用時會出現(xiàn)沖突。
  5. 如何解決菱形繼承/鉆石繼承導(dǎo)致的沖突?使用虛繼承。
  6. 什么是虛繼承?父類在繼承具有相同的祖先類時,加上virtual.

對象構(gòu)造順序總結(jié)

對象構(gòu)造順序
  • 基本原則
  1. 先父后子
  2. 從左到右
  3. 先虛后實(shí)
  4. 從上到下
  5. 由內(nèi)及外
  • 示例
#include <iostream>

using namespace std;

#define SIMPLE_CLASS(name)\
class name{\
public:\
    name(){ cout << #name << " Constructor" << endl;}\
    ~name(){ cout << #name << " Destructor" << endl;}\
};

SIMPLE_CLASS(Base1)
SIMPLE_CLASS(Base2)
SIMPLE_CLASS(Base3)

SIMPLE_CLASS(VBase1)
SIMPLE_CLASS(VBase2)
SIMPLE_CLASS(VBase3)

SIMPLE_CLASS(Member1)
SIMPLE_CLASS(Member2)
SIMPLE_CLASS(Member3)

#undef SIMPLE_CLASS

class Test : public Base1,
             public Base2,
             public Base3,
             public virtual VBase1,
             public virtual VBase2,
             public virtual VBase3 {
public:
  Test() { cout << "Test Constructor" << endl; }
  ~Test() { cout << "Test Destructor" << endl; }
private:
  Member1 m1;
  Member2 m2;
  Member3 m3;
};

int main() {
  Test t;
}
  • 執(zhí)行結(jié)果
VBase1 Constructor
VBase2 Constructor
VBase3 Constructor
Base1 Constructor
Base2 Constructor
Base3 Constructor
Member1 Constructor
Member2 Constructor
Member3 Constructor
Test Constructor
Test Destructor
Member3 Destructor
Member2 Destructor
Member1 Destructor
Base3 Destructor
Base2 Destructor
Base1 Destructor
VBase3 Destructor
VBase2 Destructor
VBase1 Destructor
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容