先來講講賦值相容規則。
前面說過,派生類如果是從基類公有繼承的,則它會包含基類中除建構函式和析構函式外的所有成員,基類的公有成員也成為派生類的公有成員,又因為物件只能訪問類的公有成員,所以基類物件具有的功能,派生類物件都有。這樣就引出了賦值相容規則。
賦值相容規則就是指在基類物件可以使用的地方都可以用公有派生類物件來代替
。注意必須是公有派生類
。賦值相容規則中的代替有三種方式,通過乙個例子分別說明。
假設有基類base,類child是base的公有派生類,base為base類的物件,pbase為base類指標,child為child類的物件。**如下:
class base
;class child : public base
;base base, *pbase;
child child;
那麼根據賦值相容規則,
可以使用類base物件的地方都可以使用類child的物件來代替
。這裡的代替有三種: 1.
派生類的物件可以賦值給基類的物件
。也就是將派生類物件從基類繼承的成員的值分別賦值給基類物件相應的成員
。例如:
base = child;
2.派生類物件的位址可以賦值給基類型別的指標
。例如:
pbase = &child;
3.派生類物件可以用來初始化基類的引用
。例如:
base &b = child;
因為有了賦值相容規則,有了上述三種賦值方式,所以
函式的引數中有基類物件或者基類指標又或者基類引用時,我們可以直接傳入派生類物件或者派生類物件的位址作為實參來執行相同的操作
。這樣的好處是什麼呢?那就是我們想對基類及派生類的物件做相同的操作時,只要定義乙個函式就行了,它的引數為基類物件或者基類指標也或者是基類引用
。這樣就大大提高了軟體開發的效率。
公有派生類物件可以代替基類物件使用,但是我們只能使用它從基類繼承的成員,而無法使用它的新添成員
。舉個例子說明下賦值相容規則:
類base為基類,類child0為base的公有派生類,類child1為類child0的公有派生類。三個類中都定義了成員函式show()。
#include
using namespace std;
class base // 基類base的宣告
// 公有成員函式show
};class child0 : public base // 類base的公有派生類child0的宣告
// 公有成員函式show
};class child1 : public child0 // 類child0的公有派生類child1的宣告
// 公有成員函式show
};void callshow(base *pbase) // 一般函式,引數為基類指標
int main()
程式執行結果為:
base::show()
base::show()
base::show()
我們首先定義了乙個函式callshow,其引數pbase為基類base型別的指標,根據賦值相容規則,我們
可以用公有派生類物件的位址為基類指標賦值
,那麼callshow函式就可以處理這個類族的所有物件
。在主函式中我們就分別把基類物件base的位址、派生類物件ch0的位址和派生類物件ch1的位址賦值給基類指標pbase,然後將pbase作為實參呼叫callshow,在callshow中呼叫了成員函式show。
但是,根據上面所講,將派生類物件的位址賦值給pbase以後,
通過pbase只能訪問派生類從基類繼承的成員
。所以即使指標pbase指向的是派生類物件ch0或者ch1
,在callshow中通過pbase也只能呼叫從基類base繼承的成員函式show
,而不會呼叫child0類或者child1類的成員函式show
。因此主函式中三次呼叫callshow函式,都是訪問的基類base的成員函式show,輸出都是base::show()。
這時我們深切的感受到,
即使派生類物件代替了基類物件,它也只能產生基類的功能,自己的新功能無法體現
。要想在代替以後同樣能夠實現自己的功能,就要用到
物件導向設計
的另乙個特性--多型性。
一.虛函式的意義
在前面賦值相容規則中給出了乙個程式例子,其中包含類base、child0和child1。在程式執行結果中我們看到,
main函式中base型別的指標pbase,分別指向base、child0和child1類的物件時呼叫的show函式都是基類base的show函式
。因為基類型別的指標指向派生類物件時,通過此指標只能訪問從基類繼承來的同名成員
。這些在前面c++賦值相容規則中已經分析過了。
但是如果我們
希望通過指向派生類物件的基類指標,訪問
派生類中
的同名成員該怎麼辦呢?
這就要用到
虛函式了。我們
在基類中將某個函式宣告為虛函式,就可以通過指向派生類物件的基類指標訪問
派生類中的同名成員了。
這樣使用某基類指標指向不同派生類的不同物件時,就可以發生不同的行為,也就實現了執行時的多型
(編譯時並不知道呼叫的是哪個類的成員)。
虛函式是動態繫結的基礎
。記住,
虛函式是非靜態的成員函式
,一定不能是
靜態(static)的成員函式
。虛函式在以後我們進行軟體架構設計時會起到很關鍵的作用。
程式設計入門
時可能不會有這方面的意識,等熟練到一定程度就會發現虛函式的強大。
二.一般虛函式成員的宣告和使用
一般的虛函式宣告形式為:
virtual 函式型別 函式名(形參表)
虛函式就是在類的宣告中用關鍵字virtual限定的成員函式
。以上宣告形式是成員函式的實現也在類的宣告中的情況。
如果成員函式的實現在類的宣告外給出時,則虛函式的宣告只能出現在類的成員函式宣告中,而不能在成員函式實現時出現
,簡而言之,
只能在此成員函式的宣告前加virtual修飾,而不能在它的實現前加。
總結下執行時多型的幾個條件:
1.類之間要滿足賦值相容規則
;2.要宣告虛函式
;3.通過類的物件的指標、引用訪問虛函式或者通過類的成員函式呼叫虛函式
。下面舉例說明下,大家通過這個例子來對照下這幾個條件。
此例是由賦值相容規則中的例子改進的。將基類中的函式show宣告為虛函式,程式其他部分不做任何修改。
#include
using namespace std;
class base // 基類base的宣告
// 虛成員函式show
};class child0 : public base // 類base的公有派生類child0的宣告
// 虛成員函式show
};class child1 : public child0 // 類child0的公有派生類child1的宣告
// 虛成員函式show
};void callshow(base *pbase) // 一般函式,引數為基類指標
int main()
程式執行結果:
base::show()
child0::show()
child1::show()
我們可以看出,僅僅是在base類中的show函式
前加了virtual的修飾,執行結果就差了很多,這正是虛函式的美麗所在。
例程中,類base、child0和child1屬於同乙個類族,而且
child0是由base公有派生的,child1是從child0公有派生的,所以滿足賦值相容規則
,這就符合了執行時多型的第乙個條件。
基類base的函式show宣告
為了虛函式
,這是第二個條件。
在callshow函式中通過物件指標pbase來訪問虛函式show
,這又滿足了第三個條件。
這個動態繫結過程在執行時完成,實現了執行時的多型
。這樣通過基類指標就可以訪問指向的不同派生類的物件的成員,這在
軟體開發
中不僅使**整齊簡潔,而且也大大提高了開發效率。
基類的成員函式宣告為虛函式以後,派生類中的同名函式可以加virtual修飾也可以不加。
三.虛析構函式
大家可能奇怪為什麼不先講虛建構函式,很簡單,因為不能宣告虛建構函式,而可以宣告虛析構函式。
多型是指
不同的物件
接收了同樣的訊息
而導致完全不同的行為
,它是針對物件而言的
,虛函式是執行時多型的基礎,當然也是針對物件的
,而建構函式
是在物件生成之前呼叫的,即執行建構函式
時還不存在物件,那麼虛建構函式也就沒有意義了。
析構函式
用於在類的物件消亡時做一些清理工作
,我們在基類中將析構函式宣告為虛函式後,其所有派生類的析構函式也都是虛函式
,使用指標引用時可以動態繫結,實現執行時多型,通過基類型別的指標就可以呼叫派生類的析構函式對派生類的物件做清理工作。
前面講過,析構函式沒有返回值型別,沒有參數列,所以虛析構函式的宣告
也比較簡單,形式如下:
virtual ~類名();
多型性 虛函式
虛函式是過載的另一種表現形式,是一種動態的過載方式。虛函式呼叫與函式體之間的聯絡在執行時才建立。c 中可以用基類的物件指標可以指向它的公有派生物件,當它指向公有派生類物件時,只能訪問派生類中從基類繼承來的成員,而不能訪問派生類中定義的成員。當指標指向不同的物件時,分別呼叫不同類的成員函式,如果將函式...
多型性和虛函式
11.27 多型指的是同樣的資訊被不同型別的物件接收導致不同的行為,包括 靜態多型性和動態多型性。靜態多型性包括 函式過載和運算子過載 動態多型主要 由虛函式實現。虛函式宣告 virsual 型別說明符 函式名 參數列 純虛函式 virtual 函式型別 函式名 參數列 0 在派生類中定義 抽象類 ...
虛函式與多型性
實驗內容 定義基類base,其資料成員為高h,定義成員函式disp 為虛函式,由基類派生出長方體類和圓柱類,並在兩個派生類中定義成員函式disp 為虛函式。在主函式中,用基類base定義指標p,用指標p動態呼叫虛函式disp 顯示面積。虛函式 它是基類中的成員函式,並在派生類中被過載。派生類中的虛函...