虛擬方法表

程序设计中的虛擬方法表(virtual method table)、虛擬函式表(virtual function table)或vtable,是一種编程语言裡支持动态分派(在運行時進行方式绑定)的方式。
若有類別定義了虚函数(或虛方法),大部份的編譯器會在此類中加入隱藏成员变量,此变量是一個指向函式陣列(即為虛擬方法表)指標。在程式運行時會使用此指標,呼叫適當的函式實現,在編譯時,還不知道要呼叫哪一個函式,或是要呼叫某個繼承此類別的類別所實現的函式。
要實現动态分派的方式有很多種,但在C++以及其他相關語言(如D語言和C#)裡常會使用虛擬方法表。一些將物件的程式介面和其實現分開的語言,像是Visual Basic和Delphi,也常使用此一作法,因為此作法可以讓物件用設定另一個方法指標的方式,就可以使用不同的函式實現。此作法可以創建外部的函式庫,其他類似的技術則不行[1]。
假設程式中包括三個有繼承關係的類別:母類別Cat,以及二個子類別HouseCat和Lion。Cat類別定義了虚函数,名稱為speak,其子類別可以提供適當的實現(例如meow或roar)。當程式呼叫Cat參考(指向Cat、HouseCat或Lion的個體),程式必須要可以確認要呼叫哪一函式。這會依物件實際的類別而定,不是參考本身的類別。此類別無法在編譯期就決定要呼叫哪一個函式,需要在運行時才動態決定要呼叫哪一個函式。
實現
[编辑]物件的虛擬方法表會包括物件動態綁定方法的記憶體位址。會透過物件的虛擬方法表,從其中方法的位址來呼叫方法。屬於同一個類別的物件會有相同的虛擬方法表,因此這些物件可以共用虛擬方法表。屬於相容類別的物件(例如繼承自同一個類別的各類別下的物件)會有相同佈置的虛擬方法表:在所有相容類別內,特定方法會在虛擬方法表中相同的位置。因此透過虛擬方法表讀取方法位置,就可以找到此物件方法的位置,以此呼叫函式[2]。
C++標準沒有強制指定動態分配實現的作法,不過各編譯器都是依照相同的基本模式,只有小部份的差異。
一般來說,編譯器會為每一個有虛擬方法的類別分別創建虛擬方法表。當產生一個物件時,這個物件會多一個隱藏成員,此成員是指向虛擬方法表的指標,也稱為虛擬方法表指標、vpointer或VPTR。因此,編譯器也需要在物件的构造器中增加隱藏程式碼,讓新物件的虛擬方法表指標指向其虛擬方法表。
許多編譯器會將虛擬方法表當作物件裡的最後一名成員,也有編譯器將虛擬方法表當作第一個成員。這兩種方式都可以產生可移殖的程式碼[3]。 g++以往就將指標當成物件的最後一名成員[4]。
相關條目
[编辑]參考資料
[编辑]- Margaret A. Ellis and Bjarne Stroustrup (1990) The Annotated C++ Reference Manual. Reading, MA: Addison-Wesley. (ISBN 0-201-51459-1)
- ^ Zendra, Olivier; Colnet, Dominique; Collin, Suzanne. Efficient Dynamic Dispatch without Virtual Function Tables: The SmallEiffel Compiler -- 12th Annual ACM SIGPLAN Conference on Object-Oriented Programming Systems, Languages and Applications (OOPSLA'97), ACM SIGPLAN, Oct 1997, Atlanta, United States. pp.125-141. inria-00565627. Centre de Recherche en Informatique de Nancy Campus Scientifique, Bâtiment LORIA. 1997: 16.
- ^ Ellis & Stroustrup 1990, pp. 227–232
- ^ Danny Kalev. "C++ Reference Guide: The Object Model II". 2003. Heading "Inheritance and Polymorphism" and "Multiple Inheritance".
- ^ C++ ABI Closed Issues. [2011-06-17]. (原始内容存档于25 July 2011). 无效
|url-status=bot: unknown
(帮助)