今天看到這篇文章
一個在C#以及類似的物件導向系統(Java中估計也是如此)中常見的錯誤,感觸頗多,說句實在話,國內目前的OO語言教學中忽略了一個關鍵的一環“OO的物件模型”,這裡有本好書:侯捷的《深度探索C++物件模型》。看過的與沒有看過的,對理解OO的物件模型有層次的不同。看過的人往往會思考:“編譯器到底做了什麼”?沒看過的會想當然:“應該是這樣的,就是這樣的......”。比如把一個子類對象強制轉換為基類對象,其實編譯器處理就是把該對象作為基類對象來處理(即對象的Slip)。當然調用的方法(非虛函數)也是基類的方法,和子類無關。而調用虛函數則先要去對象記憶體位址,再擷取對應的vTable(在C++,Delphi中都是類似處理,根據對象的地址,擷取該對象的vTable),再根據虛函數的位移量擷取vtable表中的實際虛函數的函數地址,從而實現OO的多態。
還是通過執行個體代碼來解釋吧public class BaseClass
{
public string m_Name = "BaseClass";
public void RealName()
{
Console.WriteLine(m_Name);
}
public virtual void VirtualName()
{
Console.WriteLine(m_Name);
}
}
public class DerivedClass : BaseClass
{
public string m_Name = "DerivedClass";
public void RealName()
{
Console.WriteLine(m_Name);
}
public override void VirtualName()
{
Console.WriteLine(m_Name);
}
}
public class Class2
{
[STAThread]
static void Main(string[] args)
{
BaseClass lBaseclass = new DerivedClass();
lBaseclass.RealName();//return : BaseClass
lBaseclass.VirtualName();//return : DerivedClass
Console.WriteLine("-----------------------");
DerivedClass lDerivedClass = (DerivedClass)lBaseclass;
lDerivedClass.RealName();//return : DerivedClass
lDerivedClass.VirtualName();//return : DerivedClass
Console.WriteLine("-----------------------");
lBaseclass.m_Name = "ModifyByBase";
lBaseclass.RealName();//return : ModifyByBase
lBaseclass.VirtualName();//return : DerivedClass
Console.WriteLine("-----------------------");
lDerivedClass.m_Name = "ModifyByDerived";
lDerivedClass.RealName();//return : ModifyByDerived
lDerivedClass.VirtualName();//return : ModifyByDerived
Console.WriteLine("-----------------------");
lDerivedClass = new DerivedClass();
((BaseClass)lDerivedClass).RealName();//return : BaseClass
((BaseClass)lDerivedClass).VirtualName(); //return : DerivedClass
Console.ReadLine();
}
}
上面第一段代碼的記憶體布局如所述:
第二段的代碼相對簡單,只是把用lBaseClass指向的對象強制類型轉換為子類對象,記憶體結構如所示:
第三段的代碼的記憶體結構如所示:
第四段的代碼的記憶體結構如所示:
第五段的記憶體如所示:
總之,大家要知道,等編譯完成後,所有的屬性訪問或者函數調用都已經根據變數的宣告類型都被確定了(訪問哪個記憶體位址已經確定了),除了虛函數(晚綁定)...。
比如調用lBase.VirtualName()這個虛函數,編譯器做的工作流程如下:
1,為BaseClass和DerivedClass的各個虛函數編號1(位移量)
2,lBase.VirtualName()表示調用lBase對象的vtable指標(在C++,Delphi中都是類似處理,根據對象的地址,擷取該對象的vTable)所指向的vTable表中的1號虛函數。
3,那麼如果lBase實際是指向BaseClass建立的對象,那麼就會調用BaseClass的VirtualName(),如果lBase實際是指向DerivedClass建立的對象,那麼就會調用DerivedClass的VirtualName()。則就是多態。
這裡以一句侯捷的話作為本文的結束語:“勿在浮沙築高台”。