23.C++- 繼承的多種方式、顯示調用父類構造函數、父子之間的同名函數、virtual虛函數
在C++中,繼承方式共有3種:
public繼承
-指父類的成員(變量和函數)訪問級別,在子類中保持不變
private繼承
-指父類的成員,在子類中變為private私有成員.
-也就是說子類無法訪問父類的所有成員
protected繼承
-指父類的public成員 ,在子類中變為protected保護成員,其它成員級別保持不變
<span "="" src="https://images2018.cnblogs.com/blog/1182576/201804/1182576-20180402220528771-1214130311.png" width="475" height="186">
註意: protected
比如當父類是protected繼承時,則子類的子類就無法訪問父類的所有成員
一般而言,C++項目只用到public繼承
顯示調用父類構造函數
- 當我們創建子類對象時,編譯器會默認調用父類無參構造函數
- 若有子類對象,也會默認調用子類對象的無參構造函數。
比如以下代碼:
編譯運行:
來顯示調用
接下來,修改上面子類的StrB(string s)函數,通過初始化列表調用StrA(string s)父類構造函數material可數嗎
改為:
運行打印:
父子間的同名成員和同名函數
- 子類可以定義父類中的同名成員和同名函數
- 子類中的成員變量和函數將會隱藏父類的同名成員變量和函數
- 父類中的同名成員變量和函數依然存在子類中
- 通過作用域分辨符(::)才可以訪問父類中的同名成員變量和函數
比如:
在main()函數執行:
打印:
從打印結果看到,父類和子類之間的作用域是不同的, 所以執行父類的同名成員變量和函數需要作用域分辨符(::)才行
父子間的兼容
以上示例的Parent父類Child子類為例
- 子類對象可以直接賦值給父類對象使用,比如: Parent p; Child c; p=c;
- 子類對象可以初始化父類對象,比如: Parent p1(c);
- 父類引用可以直接引用子類對象,比如: Parent& p2=c; //p2是c對象的別名
- 父類指針可以直接指向
其實是編譯器是將子類對象退化為了父類對象, 從而能通過子類來賦值初始化父類
所以上述的父類對象(包括指針/引用)也只能訪問父類中定義的成員.
如果父類對象想訪問子類的成員,只能通過強制轉換,將父類對象轉為子類類型
示例1,通過C方式轉換:
示例2,通過static_cast轉換:
首先參考下面,沒有虛函數的示例:
運行打印:
從結果看出,即使example函數的指針p指向了Child c,也只能調用父類的example(),無法實現多態性.
所以C++引入了虛函數概念,根據指針指向的對象類型,來執行不同類的同名覆蓋成員函數,實現不同的形態
定義: 在父類成員函數的返回值前面,通過virtual關鍵字聲明,這樣便能訪問子類中的同名成員函數了
接下來將上個示例的父類成員函數example()改寫為虛函數:
運行打印:
可以發現,父類和子類的長度都增加了4字節,這4個字節就是用來指向“虛函數表”的指針,編譯器便會更據這個指針來執行不同類的虛函數,實現多態性.
虛析構函數
-在使用基類指針指向派生類對象時用到
-通過基類析構函數可以刪除派生類對象
示例
運行打印:
可以發現,由於基類的析構函數是虛函數,所以我們delete基類指針時,派生類也跟著調用了析構函數,從而避免了內存泄漏,也能滿足使用dynamic_cast強制轉換了
一般而言,虛構造函數只有在繼承下才會被使用,單個類是不會使用虛構函數的,因為虛函數表會產生額外的空間
註意:構造函數不能成為虛函數,因為虛函數表是在構造函數執行後才會進行初始化
文章來源:https://www.cnblogs.com/lifexy/p/8698293.html
23.C++- 繼承的多種方式、顯示調用父類構造函數、父子之間的同名函數、virtual虛函數