助於理解。。。
虛擬函數(virtual)
使用物件導向的開發過程就是在不斷的抽象事物的過程,我們的目標就是抽象出一個緊內聚,低偶合,易於維護和擴充的模型.但是在抽象過程中我們會發現很多事物的特徵不清楚,或者很容易發生變動,怎麼辦呢?比如飛禽都有飛這個動作,但是對於不同的鳥類它的飛的動作動方式是不同的,有的要滑行,有的要顫抖翅膀, 雖然都是飛但千差萬別,在我們抽象的模型中不可能一個個都考慮到,怎麼為以後留下好的擴充,怎麼來處理子類的前差萬別?比如我現在又要抽象一個類"鶴", 它也有飛禽的特徵,我可以簡單的繼承"飛禽",而不去修改現有的代碼,可以很容易的擴充系統
物件導向的概念中引入了虛擬函數. 就是在父類中把子類中共同的,易於變化或者不清楚的特徵抽取出來,作為子類需要去重新實現的操作(override),我們可以稱做"熱點".還是上面的例子
class 飛禽
{
private void Shape ; //注意private訪問修訂符,Shape是不會被子類繼承的!
public string Wing ; //翅膀
public string Feather ; //羽毛
public virtual boolean Fly() {} ; //飛翔. 定義的虛擬函數, 這是一個熱點
}
class 麻雀 : 飛禽 //麻雀從飛禽繼承而來
{
public boolean CanSpeaking; //申明了麻雀自己的特徵.
public override boolean Fly() {...} ; //重載飛翔動作,實現自己的飛翔
}
class 鶴 : 飛禽 //鶴從飛禽繼承而來
{
public override void Fly() {...} ; //重載實現鶴的飛翔
}
//如何來使用虛擬函數,這裡同時也是一個多態的例子.
//打鳥
void ShootBird(Bird : 飛禽) //注意這裡申明傳入一個"飛禽"類,而不是具體的"鳥類". 好處是以後不管出現多少種鳥類,只要是從飛禽繼承下來的,都照打不誤:)
{
if( Bird.fly())
{
....
開始打鳥...
...
}
}
static void main()
{
//打麻雀
ShootBird( new 麻雀());
//打鶴
ShootBird( new 鶴());
//看到沒!都是打鳥的過程,我這裡可以實現打任何一種鳥了,添加一行代碼而不去修改代碼
ShootBird( new 其它的飛禽());
ShootBird( ...);
}
--------------------------------------------
虛擬函數的的執行過程:
虛擬函數從C#的程式編譯的角度來看,它和其它一般的函數有什麼區別呢?一般函數在編譯期間就靜態編譯到了執行檔案中,在程式運行期間是不發生變化的,也就是寫死了!而虛擬函數在編譯期間是不被靜態編譯的,它是不確定的,它會根據運行時期對象執行個體來動態判斷要調用的函數,其中那個申明時定義的類叫申明類,那個執行時執行個體化的類叫執行個體類.具體的檢查的流程如下:
a.當調用一個對象的函數時,系統會去檢查這個對象申明定義的類,即申明類
b.然後它更據這個申明類型的定義去檢查這個函數是否虛擬函數
c.如果有virtual關鍵字,他就認為是虛擬函數,這個時候他又去檢查執行個體類.
d.好!找到這個執行個體類後,他再檢查這個執行個體類定義中是否重新實現了虛擬函數(通過override),如果是!好了他就不再找了,馬上執行它.
e.如果沒有,系統又往上層父類找執行個體類的父類,直到找到一個最近重載了該虛擬函數的父類為止,然後執行該函數.
呵呵,知道這點有什麼用呢?搞清楚這個老師的bt題目就可以拿下了.
還是來個簡單例子說明問題
class A
{
protected virtual FuncA() {Console.WriteLine("FuncA")} ;//注意virtual,表明這是一個虛擬函數
}
class B :A //注意 B是從A類繼承,所以A是父類,B是子類
{
protected override FuncA(){Console.WriteLine("FuncB")} ; //注意override ,表明重新實現了虛擬函數
}
static void main()
{
A a ; //定義一個a這個A類對象.這個A就是a的申明類
A b ; //定義一個b這個A類的對象.這個A就是b的申明類
a = new A() ; //執行個體化a對象,A是這次執行個體類
a.FuncA() ; //開始執行FuncA. 1.先照申明類A 2.檢查是虛擬方法 3.檢查執行個體類A 4.執行執行個體類中的方法 5輸出結果 FuncA
b = new B(); //執行個體化b對象,B是這次執行個體類
b.FuncA() ; //開始執行FuncA. 1.先照申明類A 2.檢查是虛擬方法 3.檢查執行個體類B 4.執行執行個體類中的方法 5輸出結果 FuncB
}
from:http://forum.mvpcn.net/ShowPost.asp?action=Previous&id=113