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