C++中繼承相關知識
- 1.繼承的概念及定義
- 1.1繼承的概念
- 1.2繼承的定義
- 1.2.1定義格式
- 1.2.2繼承關系和訪問限定符
- 1.2.3繼承基類成員訪問方式的變化
- 2.基類和派生類物件賦值轉換(只存在于公有繼承當中)
- 3.繼承中的作用域
- 4.派生類的默認成員函式
- 5.繼承與友元
- 6.繼承與靜態成員
- 7.菱形繼承及菱形虛擬繼承
- 7.1單繼承與多繼承
- 7.2菱形繼承存在的問題及解決方案
- 7.3虛擬繼承
- 7.4虛擬繼承解決資料冗余和二義性的原理
- 8.總結與反思
- 8.1繼承和組合
1.繼承的概念及定義
1.1繼承的概念
- 繼承(inheritance)機制是面向物件程式設計使代碼可以復用的最重要的手段,它允許程式員在保持原有類特性的基礎上進行擴展,增加功能,這樣產生新的類,稱派生類,繼承呈現了面向物件程式設計的層次結構,體現了由簡單到復雜的認知程序,以前我們接觸的復用都是函式復用,繼承是類設計層次的復用
- 我們看以下代碼的運行結果
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
void Print()
{
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
protected:
string _name = "peter"; // 姓名
int _age = 18; // 年齡
};
class Student : public Person
{
protected:
int _stuid; // 學號
};
class Teacher : public Person
{
protected:
int _jobid; // 工號
};
int main()
{
Student s;
Teacher t;
s.Print();
t.Print();
return 0;
}
- 運行結果:

- 總結:繼承后父類的Person的成員(成員函式+成員變數)都會變成子類的一部分,這里體現出了Student和Teacher復用了Person的成員,
1.2繼承的定義
1.2.1定義格式


1.2.2繼承關系和訪問限定符

1.2.3繼承基類成員訪問方式的變化

- 總結:
- 基類private成員在派生類中無論以什么方式繼承都是不可見的,
這里的不可見是指基類的私有成員還是 被繼承到了派生類物件中,但是語法上限制生類物件不管在類里面還是類外面都不能去訪問它 - 基類private成員在派生類中是不能被訪問,如果基類成員不想在類外直接被訪問,但需要在派生類中能 訪問,就定義為
protected,可以看出保護成員限定符是因繼承才出現的 - 實際上面的表格我們進行一下總結會發現,基類的私有成員在子類都是不可見,基類的其他成員在子類的訪問方式 =
Min(成員在基類的訪問限定符,繼承方式),public>protected>privat - 使用關鍵字class時默認的繼承方式是private,使用struct時默認的繼承方式是public,不過最好顯示的寫出繼承方式
- 在實際運用中一般使用都是public繼承,幾乎很少用protetced/private繼承,也不提倡使用protetced/private繼承,因為protetced/private繼承下來的成員都只能在派生類的類里面使用,實際中擴展維護性不強
2.基類和派生類物件賦值轉換(只存在于公有繼承當中)
- 派生類物件可以賦值給基類的物件 / 基類的指標 / 基類的參考,這里有個形象的說法叫
切片或者切割,寓意把派生類中父類那部分切來賦值過去 - 基類物件不能賦值給派生類物件
- 基類的指標可以通過強制型別轉換賦值給派生類的指標,但是必須是基類的指標是指向派生類物件時才是安全的,這里基類如果是多型型別,可以使用 RTTI(Run-Time Type Information) 的
dynamic_cast來進行識別后進行安全轉換,

- 我們對應看三個例子:
- 賦值給基類的物件

-
賦值給基類的參考

-
賦值給基類的指標

-
總結:

3.繼承中的作用域
-
在繼承體系中
基類和派生類都有獨立的作用域(可以定義同名的變數) -
子類和父類中
有同名成員,子類成員將屏蔽父類對同名成員的直接訪問,這種情況叫隱藏,也叫重定義 -
需要注意的是如果是成員函式的隱藏,只需要函式名相同就構成隱藏
-
注意在實際中在繼承體系里面最好不要定義同名的成員
-
舉例:

4.派生類的默認成員函式
-
6個默認成員函式,“默認”的意思就是指我們不寫,編譯器會變我們自動生成一個,那么在派生類中,這幾個成員函式是如何生成的呢?
-
Ⅰ.建構式:派生類的建構式必須呼叫基類的建構式初始化基類的那一部分成員,如果基類沒有默認的建構式,則必須在派生類建構式的初始化串列階段顯示呼叫

-
Ⅱ.拷貝構造: 派生類的拷貝建構式必須呼叫基類的拷貝構造完成基類的拷貝初始化

-
Ⅲ. 賦值運算子多載:派生類的operator=必須要呼叫基類的operator=完成基類的賦值

-
Ⅳ.派生類的解構式會在被呼叫完成后自動呼叫基類的解構式清理基類成員,因為這樣才能保證派生類物件先清理派生類成員再清理基類成員的順序(如果自己顯式呼叫,會存在父類先析構問題)


-
Ⅴ.派生類物件初始化先呼叫基類構造再調派生類構造 -
Ⅵ.派生類物件析構清理先呼叫派生類析構再調基類的析構

5.繼承與友元
- 友元關系不能繼承,也就是說基類友元不能訪問子類私有和保護成員

6.繼承與靜態成員
- 基類定義了static靜態成員,則整個繼承體系里面只有一個這樣的成員,無論派生出多少個子類,都
只有一個static成員實體

7.菱形繼承及菱形虛擬繼承
7.1單繼承與多繼承
- 單繼承:一個子類只有一個直接父類時稱這個繼承關系為單繼承

- 多繼承:一個子類有兩個或以上直接父類時稱這個繼承關系為多繼承

- 菱形繼承:菱形繼承是多繼承的一種特殊情況(算是多繼承中的一種缺陷)

7.2菱形繼承存在的問題及解決方案

- 這里
加入域限定符只能起到治標不治本的作用 - 那么我們如何解決資料冗余的問題呢,這里我們引入虛擬繼承,即可解決問題
7.3虛擬繼承
- 虛擬繼承可以解決菱形繼承的二義性和資料冗余的問題,如上面的繼承關系,在Student和Teacher的繼承Person時使用虛擬繼承,即可解決問題

7.4虛擬繼承解決資料冗余和二義性的原理
- 先分析菱形繼承

- 再分析菱形虛擬繼承

- 總結:這里可以分析出D物件中將A放到的了物件組成的最下面,這個A
同時屬于B和C,那么B和C如何去找到公共的A呢? 這里是通過了B和C的兩個指標,指向的一張表,這兩個指標叫虛基表指標,這兩個表叫虛基表,虛基表中存的偏移量,通過偏移量可以找到下面的A
8.總結與反思
- 多繼承中存在菱形繼承,菱形繼承就存在資料冗余和二義性的缺陷,那么解決這兩個缺陷的方法就是虛擬繼承,所以一般不要設計出菱形繼承,否在在復雜度及性能上都有問題
- 多繼承是C++的缺陷之一
8.1繼承和組合
-
繼承就是
is-a,繼承一定程度破壞了基類的封裝,基類的改變,對派生類有很大的影響,派生類和基類間的依賴關系很強,耦合度高

-
組合是
has-a,因為物件的內部細節是不可見的,物件只以“黑箱”的形式出現, 組合類之間沒有很強的依賴關系,耦合度低,優先使用物件組合有助于你保持每個類被封裝

-
優先使用物件組合,而不是類繼承
-
繼承和組合都是類設計角度的復用
-
在軟體工程中用組合更好,組合更符合
高類聚,低耦合,更符合is-a關系用繼承,更符合has-a用組合,都符合的情況用組合
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/282655.html
標籤:其他
