虚拟方法表

程序设计中的虚拟方法表(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
(帮助)