天天看點

标準C++語言-類的繼承Ⅱ一、子類的構造、析構、拷貝二、私有繼承、保護繼承三、多重繼承、鑽石繼承、虛繼承四、虛函數、覆寫、多态五、覆寫和多态的條件六、純虛函數和抽象類

一、子類的構造、析構、拷貝

  • 子類的構造
    • 執行子類的構造函數前會根據繼承表的順序執行父類的構造函數
    • 預設執行父類的無參構造
    • 顯式調用有參構造,在子類的構造函數後,初始化清單中顯式調用父類的有參構造函數
  • 子類的析構
    • 子類的析構函數執行後,會根據繼承表的順序逆序執行父類的析構函數
    • 注意:父類的指針可以指向子類對象,當通過父類指針釋放對象時,隻會調用父類的析構函數,而這種析構方式有可能造成記憶體洩漏
  • 子類的拷貝
    • 當使用子類對象來初始化新的子類對象時,會自動調用子類預設的拷貝構造函數,并且會先調用父類預設的拷貝構造函數
    • 子類中實作的拷貝構造,需要顯式調用父類拷貝構造,否則就會調用無參構造

二、私有繼承、保護繼承

  • 使用private方式繼承父類
    • 公有的變成私有的,其他的不變,這種繼承方式防止父類的成員擴散
  • 使用 protected 方式繼承父類
    • 公有的變成保護的,其他的不變,這種繼承方式可以防止父類的成員擴散
  • 子類以私有或保護方式繼承父類,禁止向上造型
    • 子類的指針或引用不能隐式轉換成父類的指針或引用,要想實作多态隻能以公開方式繼承父類

三、多重繼承、鑽石繼承、虛繼承

  • 多重載繼承
    • C++中一個子類可以有多個父類,在繼承表中按照順序繼承多個父類的屬性和行為,并按照順序表中的調用父類的構造函數
    • 按照從低到高的位址順序排列父類,子類中會标記每個父類存儲位置
    • 當子類指針轉換成父類的隐式指針時,編譯器會自動計算父類中的記憶體所在子類中的位置,位址會自動進行偏移計算
  • 名字沖突
    • 如果父類中有同名的成員,可以正常繼承
    • 但如果直接使用,會造成歧義,需要

      類名::成員名

      進行通路
  • 鑽石繼承
    • 例如:一個類A,類B繼承類A,類C也繼承類A,然後類D繼承類B和類C
    • 例如:一個子類繼承多個父類,這些父類有一個共同的祖先
    • 注意:鑽石繼承不會導緻繼承錯誤,但通路祖先類中的成員時每次需要使用

      類名::成員名

      ,這種繼承會造成備援
  • 虛繼承

    virtual

    • 當進行鑽石繼承時,祖先類中的内容會有備援,而進行虛繼承後,在子類中的内容和構造函數隻會保留一份
    • 注意:但使用虛繼承時子類中會多了一些内容(指向從祖先類繼承來的成員)
  • 構造函數:
    • 一旦進行了虛繼承(鑽石繼承),祖先類的構造函數隻執行一次,由孫子類直接調用,祖先類的有參構造也需要在孫子類中顯式調用
  • 拷貝構造
    • 在虛繼承(鑽石繼承)中祖先的的拷貝構造由孫子類直接調用,子類中不再調用祖先的拷貝構造,在手動實作的拷貝構造時(深拷貝),祖先類中的内容也由孫子類負責拷貝
    • 同理指派構造也一樣

四、虛函數、覆寫、多态

  • 虛函數
    • 類的成員函數前加

      virtual

  • 覆寫
    • 子類會覆寫父類的虛函數
  • 多态
    • 當子類覆寫了父類的虛函數時,通過父類指針指向子類對象時,調用虛函數,會根據具體的對象是誰來決定執行誰的函數

五、覆寫和多态的條件

  • 覆寫的條件:
    • 必須是虛函數
    • 必須在父子類之間
    • 函數簽名必須相同(參數清單完全一緻 const 需要一緻)
    • 傳回值必須是同類或者父子類(子類的傳回值要能向父類的隐式轉換)
    • 通路屬性不會影響覆寫
    • 常函數屬性會影響覆寫
  • 重載、隐藏、覆寫(重寫)的差別
    • 重載:發生在同一作用域下的同名函數,參數不同,函數簽名不同,構成重載
    • 隐藏:發生在父子不同作用域下的同名成員,如果沒有形成覆寫,且能通過編譯,必定是隐藏
    • 覆寫(重寫):發生在父子不同作用域下的虛函數修飾的父類函數
  • 多态的條件
    • 父子類之間有的函數有覆寫關系
    • 父類的指針或引用指向子類的對象
    • 注意:在父類的構造函數中調用虛函數,此時子類還沒有建立完成,是以隻能調用父類的虛函數,而不是覆寫版本的虛函數

六、純虛函數和抽象類

  • 純虛函數
    • 在純虛函數的聲明的後面添加

      =0

      ,不可實作
    • 用法:

      virtual 傳回值 函數名(參數) = 0

    • 注意:一定要實作的話,隻能在類外部實作,且在父類的構造函數和析構函數中才能調用
  • 抽象類
    • 抽象類的成員函數中有純虛函數
    • 抽象類不能執行個體化(不能建立對象)
    • 抽象類被繼承,純虛函數被覆寫,由子類執行個體化對象
    • 若子類沒有覆寫純虛函數,子類也将成為抽象類,也不能執行個體化
  • 純抽象類
    • 純抽象類的所有成員函數都是純虛函數
    • 這種類一般用來設計接口,這種類在子類被替換後不需要修改或少量的修改即可繼續使用
  • 純抽象類的應用:工廠類模式
#include <iostream>
using namespace std;

class Base{
public:
	virtual void show(void) = 0;
};
class A:public Base{
public:
	void show(void){
		cout << "我是類A的show函數" << endl;
	}
};
class B:public Base{
public:
	void show(void){
		cout << "我是類B的show函數" << endl;
	}
};
class C:public Base{
public:
	void show(void){
		cout << "我是類C的show函數" << endl;
	}
};
enum ClassType{typeA,typeB,typeC};

//大名鼎鼎的工廠類模式
Base* creat_object(ClassType type){
	switch(type)	{
		case typeA: return new A;
		case typeB: return new B;
		case typeC: return new C;
		default: return NULL;
	}
}
int main(){
	Base* p = creat_object(typeC);
	p->show();
}
           

繼續閱讀