C 高階學習系列 多型

2021-10-02 07:51:26 字數 3437 閱讀 1049

多型是指通過基類的指標既可以訪問基類的成員,也可以訪問派生類的成員。換句話說,基類指標可以按照基類的方式來做事,也可以按照派生類的方式來做事,它有多種形態,或者說有多種表現方式,我們將這種現象稱為多型(polymorphism)

c++提供多型的目的是:可以通過基類指針對所有派生類(包括直接派生和間接派生)的成員變數和成員函式進行「全方位」的訪問,尤其是成員函式。如果沒有多型,我們只能訪問成員變數。

下面是構成多型的條件:

下面的例子對各種混亂情形進行了演示:

#include using namespace std;

//基類base

class base;

void base::func()

在基類 base 中我們將void func()宣告為虛函式,這樣派生類 derived 中的void func()就會自動成為虛函式。p 是基類 base 的指標,但是指向了派生類 derived 的物件。

語句p -> func();呼叫的是派生類的虛函式,構成了多型。

語句p -> func(10);呼叫的是基類的虛函式,因為派生類中沒有函式覆蓋它。

語句p -> func("");出現編譯錯誤,因為通過基類的指標只能訪問從基類繼承過去的成員,不能訪問派生類新增的成員。

虛函式是在基類中使用關鍵字virtual宣告的函式。在派生類中重新定義基類中定義的虛函式時,會告訴編譯器不要靜態鏈結到該函式(早繫結或靜態多型--函式呼叫在程式執行前就準備好了)。

多型是物件導向程式設計的主要特徵之一,c++中虛函式的唯一用處就是構成多型。

舉個例子, 我們知道基類的指標是可以指向派生類物件的:

#include using namespace std;

//基類people

class people;

people::people(char *name, int age): m_name(name), m_age(age){}

void people::display()

**輸出結果為:

王志剛今年23歲了,是個無業遊民。

趙巨集佳今年45歲了,是個無業遊民。

輸出結果告訴我們,通過基類指標只能訪問派生類的成員變數,但是不能訪問派生類的成員函式。所以導致輸出結果偏離預期。

為了讓基類指標能夠訪問派生類的成員函式,必須使用虛函式(virtual function)。更改上面的**,將基類中 display() 宣告為虛函式即可 (這樣 在程式中任意點可以根據所呼叫的物件型別來選擇呼叫的函式,這種操作被稱為動態鏈結,或後期繫結) :

#include using namespace std;

//基類people

class people;

people::people(char *name, int age): m_name(name), m_age(age){}

void people::display()

**輸出結果為:

王志剛今年23歲了,是個無業遊民。

趙巨集佳今年45歲了,是一名教師,每月有8200元的收入。

在之前的文章中我們說過,通過指標呼叫普通的成員函式時會根據指標的型別(通過哪個類定義的指標)來判斷呼叫哪個類的成員函式,但是這裡通過分析可以發現,這種說法並不適用於虛函式,虛函式是根據指標的指向來呼叫的,指標指向哪個類的物件就呼叫哪個類的虛函式。

c++ 虛函式對於多型具有決定性的作用,有虛函式才能構成多型 , 虛函式的注意事項如下 : 

1) virtual 關鍵字只需要在基類中的虛函式的宣告處加上 ,定義處無所謂加與不加。

2) 當在基類中定義了虛函式時,如果派生類沒有定義新的同名同參函式來遮蔽此函式,那麼呼叫時候將使用基類的虛函式。

3)  只有派生類的虛函式覆蓋基類的虛函式(相同的函式原型 ( 返回型別 函式名 引數列表 ))才能構成多型(通過基類指標或者引用訪問派生類函式)。

4)建構函式不能是虛函式。對於基類的建構函式,它僅僅是在派生類建構函式中被呼叫,這種機制不同於繼承。也就是說,派生類不繼承基類的建構函式,將建構函式宣告為虛函式沒有什麼意義。

5) 析構函式可以宣告為虛函式,而且有時候必須要宣告為虛函式。

首先看成員函式所在的類是否會作為基類。然後看成員函式在類的繼承後有無可能被更改功能,如果希望更改其功能的,一般應該將它宣告為虛函式。如果成員函式在類被繼承後功能不需修改,或派生類用不到該函式,則不要把它宣告為虛函式。

若在基類中又不想對虛函式給出有意義的實現 ( 基類中只進行宣告 ) ,可以使用純虛函式。語法格式為:

virtual 返回值型別 函式名 (函式引數) = 0;
最後的=0並不表示函式返回值為0,它只起形式上的作用,告訴編譯系統「這是純虛函式」。

只要包含純虛函式的類就稱為抽象類(abstract class)。之所以說它抽象,是因為它無法例項化,也就是無法建立物件。原因很明顯,純虛函式沒有函式體,不是完整的函式,無法呼叫,也無法為其分配記憶體空間。包含純虛函式的抽象類為派生類提供了「約束條件」,派生類必須要實現抽象類中的純虛函式,否則就不能例項化為物件。

在實際開發中,你可以定義乙個抽象基類,只完成部分功能,未完成的功能交給派生類去實現(誰派生誰實現)。這部分未完成的功能,往往是基類不需要的,或者在基類中無法實現的。雖然抽象基類沒有完成,但是卻強制要求派生類完成,這就是抽象基類的「霸王條款」。抽象基類除了約束派生類的功能,同樣是可以實現多型 ( 基類的指標可以訪問派生類的成員 ) 。

然而普通成員函式和頂層函式 ( 即最外層函式)均不能宣告為純虛函式 , 如下所示:

//頂層函式不能被宣告為純虛函式

void fun() = 0; //compile error

class base;

引用在本質上是通過指標的方式實現的,修改上例中 main() 函式內部的**,用引用取代指標:

int main()
由於引用類似於常量,只能在定義的同時初始化,並且不能再引用其他資料,所以引用需要定義初始化基類和派生類的所有物件。引用不像指標靈活,指標可以隨時改變指向,而引用只能指代固定的物件 ( 有多少物件就要建立多少個引用,在多型性方面缺乏表現力,所以以後我們再談及多型實現時一般是說使用指標。

C 學習筆記之多型 多型的學習 多型學習

c 學習筆記之多型 多型的學習 多型學習 多型分為兩類 靜態多型 函式過載和運算子過載屬於靜態多型,復用函式名 動態多型 派生類和虛函式實現執行時多型 靜態多型和動態多型的區別 靜態多型的函式位址早繫結 編譯階段確定函式位址 動態多型的函式位址晚繫結 執行階段確定函式位址 動態多型滿足條件 動態多型...

C 多型學習

純虛函式,0是告訴編譯器函式沒有主體 virtual int area 0 這個學習中有個點不懂,是關於指標的,先看 class shape virtual int area 純虛函式 virtual int area 0 class rectangle public shape int area ...

Python高階 多型

物件導向三大特徵 封裝 繼承 多型 封裝 將資料和方法放在乙個類中就構成了封裝 繼承 python中乙個類可以繼承於乙個類也可以繼承多個類,被繼承的類叫做父類 或叫基類,base class 繼承的類叫子類 多型 指的是一類事物有多種形態,乙個抽象類有多個子類 因而多型的概念依賴於繼承 不同的子類物...