1. 程式人生 > >23.C++- 繼承的多種方式、顯示調用父類構造函數、父子之間的同名函數、virtual虛函數

23.C++- 繼承的多種方式、顯示調用父類構造函數、父子之間的同名函數、virtual虛函數

vat 子類 name png 才會 項目 作用域 static 創建子類


在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對象的別名
  • 父類指針可以直接指向
    子類對象,比如: Parent* p3=&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虛函數