淺談使用繼承的虛方法調用原理


      最近看書的時候看到了虛方法調用這一塊,所以溫習一下這塊的知識,和大家分享一下。

調用虛方法時,具體調用的哪個方法不是在編譯時定的,而是在運行時根據對象的真實類型而定的,因此,CLR對於虛方法調用采用了動態分派的方法

      舉兩個例子,定義兩個繼承關系的類Parent和Child

 1 class Parent
2 {
3 public virtual void virtualMtd()
4 {
5 Console.WriteLine("Parent");
6 }
7 }
8 class Child : Parent
9 {
10 public override void virtualMtd()
11 {
12 Console.WriteLine("Child");
13 }
14 }

      Parent 類中定義了一個虛方法virtualMtd(),Child類中重寫了此方法。

      Main函數的代碼如下

 1 static void Main(string[] args)
2 {
3
4 Parent p = new Parent();
5 p.virtualMtd();
6 Child c = new Child();
7 c.virtualMtd();
8 p = c;
9 p.virtualMtd();
10 }

     在main方法中一共有3處調用了virtualMtd()。編譯一下。

     在使用ildasm查看程序入口Mian方法的IL代碼如下:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // 代碼大小       37 (0x25)
  .maxstack  1
  .locals init ([0] class ConsoleApplication2.Parent p,
           [1] class ConsoleApplication2.Child c)
  IL_0000:  nop
  IL_0001:  newobj     instance void ConsoleApplication2.Parent::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  callvirt   instance void ConsoleApplication2.Parent::virtualMtd()
  IL_000d:  nop
  IL_000e:  newobj     instance void ConsoleApplication2.Child::.ctor()
  IL_0013:  stloc.1
  IL_0014:  ldloc.1
  IL_0015:  callvirt   instance void ConsoleApplication2.Parent::virtualMtd()
  IL_001a:  nop
  IL_001b:  ldloc.1
  IL_001c:  stloc.0
  IL_001d:  ldloc.0
  IL_001e:  callvirt   instance void ConsoleApplication2.Parent::virtualMtd()
  IL_0023:  nop
  IL_0024:  ret
} // end of method Program::Main

 

    請注意加顏色的三行分別對應下邊的2,4,6

1  Parent p = new Parent();
2 p.virtualMtd();
3 Child c = new Child();
4 c.virtualMtd();
5 p = c;
6 p.virtualMtd();

 

      可以看到在C#的源代碼中的三句話不一樣,但生成的IL語句都是一樣的。

      通過向堆棧中壓入不同對象引用,3條一樣callvirt指令將調用所引用對象的同名方法,這正是虛方法的調用實質。

其實在Parent類類型方法表里新添加一個行:虛方法virtualMtd()在子類類型方法表中也有一個virtualMtd(),並且他們在各自的類型方法表里的保存的位置也是一樣的。

IL_0015:  callvirt   instance void ConsoleApplication2.Parent::virtualMtd()

      CLR在執行IL指令時,會根據Parent類型表中virtualMtd方法的位置得到一個索引號,然后,根據執行此方法的對象的真實類型查對應的類型表(當前對象為Child類型,則查Child類型表),按前面得到的索引到方法表中找到應該調用的方法。

總結一句話:按同樣的索引查不同的表。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM