繼承是面向對象的三大特性之一
定義類時,下級别的成員除了擁有上一級的共性,還有自己的特性
6.1 繼承的基本文法
作用:減少代碼重複量
文法:
class 子類 : 繼承方式 父類
例:
class MyPage : public BasePage
子類也稱為派生類、父類也稱為基類
派生類中的成員,包含兩大部分:一類是從基類繼承過來的,一類是自己增加的成員。從基類繼承過來的表現其共性,新增的表現其個性
6.2 繼承方式
繼承方式一共有三種:
- 公共繼承
- 父類中的公共權限成員,子類中也是公共權限
- 父類中的保護權限成員,子類中也是保護權限
- 父類的私有權限成員,子類無法通路
- 保護繼承
- 父類中的公共權限成員,子類中也是保護權限
- 父類中的保護權限成員,子類中也是保護權限
- 父類的私有權限成員,子類無法通路
- 私有繼承
- 父類中的公共權限成員,子類中是私有權限
- 父類中的保護權限成員,子類中是私有權限
- 父類的私有權限成員,子類無法通路
【C++筆記】繼承
6.3 繼承中的對象模型
父類中所有非靜态成員屬性都會被子類繼承下去。父類中私有成員屬性,是被編譯器隐藏了,是以是通路不到,但是确實被繼承下去了。
//例:
class Base{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son : public Base{
public:
int m_D;
};
void test(){
cout << "size of Son = " << sizeof(Son) << endl;
//輸出結果:16
}
6.4 繼承中構造和析構順序
子類繼承父類後,建立子類對象,也會調用父類的構造函數
繼承中的構造和析構順序如下:
- 構造:先構造父類,在構造子類
- 析構:順序與構造相反
//例:
class Base{
public:
Base(){
cout << "Base的構造函數" << endl;
}
~Base(){
cout << "Base的析構函數" << endl;
}
};
class Son : public Base{
public:
Son(){
cout << "Son的構造函數" << endl;
}
~Son(){
cout << "Son的析構函數" << endl;
}
};
void test(){
Son s;
}
------------
//輸出
Base的構造函數
Son的構造函數
Son的析構函數
Base的析構函數
6.5 繼承同名成員處理方式
- 通路子類同名成員,直接通路即可
- 通路父類同名成員,需要加作用域
例:
s.Base::m_A
或
s.Base::func()
//測試案例
#include <iostream>
using namespace std;
class Base{
public:
Base(){
m_A = 100;
}
void func(){
cout << "Base func 函數調用" << endl;
}
int m_A;
};
class Son : public Base{
public:
Son(){
m_A = 200;
}
void func(){
cout << "Son func 函數調用" << endl;
}
int m_A;
};
void test01(){
Son s;
cout << "Son : m_A = " << s.m_A << endl;
cout << "Base : m_A = " << s.Base::m_A << endl;
}
void test02(){
Son s;
s.func();
s.Base::func();
}
int main(){
test02();
system("pause");
return 0;
}
注意事項:
如果子類中出現和父類同名的成員函數,子類中的同名成員會隐藏掉父類中所有同名成員函數(包括重載)。如果先要通路父類同名成員,需要加作用域
6.6 繼承同名靜态成員處理方式
靜态成員和非靜态成員出現同名,處理方式一緻
- 通路子類同名成員,直接通路即可
- 通路父類同名成員,需要加作用域
#include <iostream>
using namespace std;
class Base{
public:
static int m_A;
static void func(){
cout << "Base - static void func" << endl;
}
};
int Base::m_A = 100;
class Son : public Base{
public:
static int m_A;
static void func(){
cout << "Son - static void func" << endl;
}
};
int Son::m_A = 200;
void test01(){
//通過對象通路
Son s;
cout << "Son - m_A = " << s.m_A << endl;
cout << "Base - m_A = " << s.Base::m_A << endl;
//通過類名通路
cout << "Son - m_A = " << Son::m_A << endl;
//第一個::代表通過類名方式通路 第二個::代表通路父類作用域下的成員
cout << "Base - m_A = " << Son::Base::m_A << endl;
}
void test02(){
//通過對象通路
Son s;
s.func();
s.Base::func();
//通過類名通路
Son::func();
Son::Base::func();
}
int main(){
test02();
system("Pause");
return 0;
}
6.7 多繼承文法
C++允許一個類繼承多個類
文法:
class 子類 : 繼承方式 父類1, 繼承方式 父類2...
//例:
class Son : public Base1, public Base2{
如果多繼承中父類出現了同名情況,子類使用時要加作用域
//例:
cout << "Base1 - m_A = " << s.Base1::m_A << endl;
cout << "Base2 - m_A = " << s.Base2::m_A << endl;
6.8 菱形繼承
概念:
- 兩個派生類繼承同一個類
- 又有某個類同時繼承這兩個類
- 這種繼承稱為菱形繼承
菱形繼承的問題:
- Son1繼承了Base的資料,Son2也繼承了Base的資料,當GrandSon使用資料時,就會産生二義性
解決方法:兩個父類因為相同的資料,需要加以作用域區分
g.Son1::m_A = 100
- GrandSon繼承來自Base的資料有兩份,但實際上有一份就可以
利用虛繼承,解決菱形繼承資料重複的問題。發生虛繼承之後,資料隻有一個,且可以不加作用域直接通路。
在繼承前加上關鍵字virtual。此時的Base稱為虛基類。
class Son1 : virtual public Base
參考:黑馬程式員匠心之作|C++教程從0到1入門程式設計,學習程式設計不再難
連結:https://www.bilibili.com/video/BV1et411b73Z