Ағылшыншамен салыстырыңыз: абзацты басыңыз — түпнұсқа терезеде ашылады. Абзац астындағы EN түймесі оны мәтін ішінде көрсетеді.
Мазмұны
Кіріспе
Динамикалық жіберуді қолдау механизмі
Mechanism for supporting dynamic dispatch
Компьютерлік бағдарламалауда виртуалды әдіс кестесі (ВМТ), виртуалды функция кестесі, виртуалды шақыру кестесі, жіберу кестесі, vtable немесе vftable – динамикалық жіберуді (немесе орындалу уақытында әдіс байлауды) қолдау үшін бағдарламалау тілінде қолданылатын механизм. Класс виртуалды функцияны (немесе әдісті) анықтағанда, көптеген компиляторлар сол классқа виртуалды әдіс кестесі деп аталатын (виртуалды) функцияларға сілтемелер массивіне сілтеме жасайтын жасырын мүше айнымалысын қосады. Бұл көрсеткіштер орындалу кезінде тиісті функцияны шақыру үшін қолданылады, себебі компиляция кезінде негізгі функция шақырылуы керек пе, әлде негізгі кластан мұрагерлік алған кластың іске асырған туынды функциясы шақырылуы керек пе, әлі белгісіз болуы мүмкін. Мұндай динамикалық жіберуді жүзеге асырудың көптеген түрлі жолдары бар, бірақ виртуалды әдіс кестелерін қолдану C++ және оған ұқсас тілдерде (мысалы, D және C#) ерекше кең таралған. Visual Basic және Delphi сияқты объектілердің бағдарламалық интерфейсін іске асырудан бөлетін тілдер де осы тәсілді қолданады, өйткені ол объектілерге әдіс сілтемелерінің басқа жиынтығын пайдалану арқылы басқа іске асыруды қолдануға мүмкіндік береді. Бұл тәсіл сыртқы кітапханаларды құруға мүмкіндік береді, ал басқа тәсілдерде бұл мүмкін болмауы мүмкін. Егер бағдарлама мұрагерлік иерархияда үш класс қамтыса: суперкласс, және екі субкласс, ал класс виртуалды функцияны анықтаса, онда оның субкластары тиісті іске асыруды ұсынуы мүмкін (мысалы, немесе ). Бағдарлама функцияны сілтеме арқылы шақырғанда (ол , немесе -ның бір данасына сілтеме жасай алады), код функцияның қай нұсқасына жіберу керектігін анықтай білуі керек. Бұл объектінің нақты класына байланысты, сілтеме класына емес. Класты әдетте статикалық түрде (яғни, компиляция кезінде) анықтау мүмкін емес, сондықтан компилятор сол кезде қай функцияны шақыру керектігін шеше алмайды. Шақыру дұрыс функцияға динамикалық түрде (яғни, орындалу кезінде) жіберілуі керек.
In computer programming, a virtual method table (VMT), virtual function table, virtual call table, dispatch table, vtable, or vftable is a mechanism used in a programming language to support dynamic dispatch (or run time method binding). Whenever a class defines a virtual function (or method), most compilers add a hidden member variable to the class that points to an array of pointers to (virtual) functions called the virtual method table. These pointers are used at runtime to invoke the appropriate function implementations, because at compile time it may not yet be known if the base function is to be called or a derived one implemented by a class that inherits from the base class. There are many different ways to implement such dynamic dispatch, but use of virtual method tables is especially common among C++ and related languages (such as D and C#). Languages that separate the programmatic interface of objects from the implementation, like Visual Basic and Delphi, also tend to use this approach, because it allows objects to use a different implementation simply by using a different set of method pointers. The method allows creation of external libraries, where other techniques perhaps may not. Suppose a program contains three classes in an inheritance hierarchy: a superclass, , and two subclasses, and Class defines a virtual function named , so its subclasses may provide an appropriate implementation (e. g. either or ). When the program calls the function on a reference (which can refer to an instance of , or an instance of or ), the code must be able to determine which implementation of the function the call should be dispatched to. This depends on the actual class of the object, not the class of the reference to it The class cannot generally be determined statically (that is, at compile time), so neither can the compiler decide which function to call at that time. The call must be dispatched to the right function dynamically (that is, at run time) instead.
Іске асыру
Объектінің виртуалды әдіс кестесі объектінің динамикалық байланысты әдістерінің мекенжайларын қамтиды. Әдіс шақырулары әдістің мекенжайын объектінің виртуалды әдіс кестесінен алу арқылы жүзеге асырылады. Виртуалды әдіс кестесі бір сыныпқа жататын барлық объектілер үшін бірдей, сондықтан олардың арасында ортақ пайдаланылады. Мұрагерлік иерархиясындағы туыс сыныптар сияқты, типтік үйлесімді сыныптарға жататын объектілерде бірдей құрылымы бар виртуалды әдіс кестелері болады: белгілі бір әдістің мекенжайы барлық типтік үйлесімді сыныптар үшін бірдей смещениеде орналасады. Осылайша, виртуалды әдіс кестесінен белгілі бір смещение бойынша әдістің мекенжайын алу, объектінің нақты класына сәйкес әдісті береді. C++ стандарттары динамикалық шақырудың қалай жүзеге асырылуын нақты талап етпейді, бірақ компиляторлар көбінесе бірдей негізгі модельдің шағын өзгерістерін қолданады. Әдетте, компилятор әр сынып үшін жеке виртуалды әдіс кестесін жасайды. Объект құрылған кезде, осы кестеге көрсеткіш, vpointer немесе VPTR деп аталатын сілтеме, объектінің жасырын мүшесі ретінде қосылады. Сондықтан, компилятор әр сыныптың конструкторларында жаңа объектінің виртуалды кесте көрсеткішін оның класының виртуалды әдіс кестесінің мекенжайына инициализациялау үшін "жасырын" кодты да жасауы керек. Көптеген компиляторлар виртуалды кесте көрсеткішін объектінің соңғы мүшесі ретінде орналастырады, ал басқалары – бірінші мүшесі ретінде; портативті код екі жағдайда да жұмыс істейді. Мысалы, g++ бұрын көрсеткішті объектінің соңына орналастырған.
An object's virtual method table will contain the addresses of the object's dynamically bound methods. Method calls are performed by fetching the method's address from the object's virtual method table. The virtual method table is the same for all objects belonging to the same class, and is therefore typically shared between them. Objects belonging to type compatible classes (for example siblings in an inheritance hierarchy) will have virtual method tables with the same layout: the address of a given method will appear at the same offset for all type compatible classes. Thus, fetching the method's address from a given offset into a virtual method table will get the method corresponding to the object's actual class. The C++ standards do not mandate exactly how dynamic dispatch must be implemented, but compilers generally use minor variations on the same basic model. Typically, the compiler creates a separate virtual method table for each class. When an object is created, a pointer to this table, called the virtual table pointer, vpointer or VPTR, is added as a hidden member of this object. As such, the compiler must also generate "hidden" code in the constructors of each class to initialize a new object's virtual table pointer to the address of its class's virtual method table. Many compilers place the virtual table pointer as the last member of the object; other compilers place it as the first; portable source code works either way. For example, g++ previously placed the pointer at the end of the object.
Тиімділік
Виртуалды шақыру, виртуалды емес шақырумен салыстырғанда, кем дегенде бір қосымша индекстелген сілтемеден өтуді және кейде "реттеу" операциясын қажет етеді. Виртуалды емес шақыру – компиляцияланған мекенжайға тікелей секіру. Сондықтан, виртуалды функцияларды шақыру виртуалды емес функцияларды шақырудан өте баяу. 1996 жылы жасалған тәжірибе көрсеткендей, орындалу уақытының шамамен 6–13% дұрыс функцияны табуға жұмсалады, бірақ бұл көрсеткіш 50%-ға дейін жете алады. Қазіргі заманғы процессорлардың (Орталық өңдеу блогы) үлкен кэші мен жақсырақ тармақталу болжамы виртуалды функциялардың құнын азайтуы мүмкін. Сонымен қатар, JIT компиляциясы қолданылмаған жағдайларда, виртуалды функцияларды шақыруды инлайнға қосу әдетте мүмкін емес. Кейбір жағдайларда компилятор "девиртуализация" деп аталатын процесті орындай алады, онда іздеу және жанама шақыру әрбір инлайнды код блогының шартты орындалуымен алмастырылады, бірақ мұндай оңтайландырулар сирек кездеседі. Бұл қосымша жүктемеден аулақтау үшін компиляторлар, шақыру компиляция уақытында шешілген жағдайларда, виртуалды әдіс кестелерін пайдаланудан қашады. Мысалы, жоғарыдағы f1 шақыруы кестеден іздеуді қажет етпеуі мүмкін, өйткені компилятор d айнымалысы тек D класын ғана ұстай алатынын және D класы f1 функциясын қайта жазбайтынын анықтай алады. Немесе компилятор (немесе оптимизатор) B1 класының f1 функциясын қайта жазатын ешбір түнгі класы бағдарламада жоқ екенін анықтауы мүмкін. B1::f1 немесе B2::f2 шақырулары кестеден іздеуді қажет етпеуі мүмкін, өйткені орындалуы нақты көрсетілген (бірақ "this" көрсеткішін реттеу әлі де қажет).
A virtual call requires at least an extra indexed dereference and sometimes a "fixup" addition, compared to a non virtual call, which is simply a jump to a compiled in pointer. Therefore, calling virtual functions is inherently slower than calling non virtual functions. An experiment done in 1996 indicates that approximately 6–13% of execution time is spent simply dispatching to the correct function, though the overhead can be as high as 50%. The cost of virtual functions may not be so high on modern CPU (Central Processing Unit) architectures due to much larger caches and better branch prediction. Furthermore, in environments where JIT compilation is not in use, virtual function calls usually cannot be inlined. In certain cases it may be possible for the compiler to perform a process known as devirtualization in which, for instance, the lookup and indirect call are replaced with a conditional execution of each inlined body, but such optimizations are not common. To avoid this overhead, compilers usually avoid using virtual method tables whenever the call can be resolved at compile time. Thus, the call to f1 above may not require a table lookup because the compiler may be able to tell that d can only hold a D at this point, and D does not override f1. Or the compiler (or optimizer) may be able to detect that there are no subclasses of B1 anywhere in the program that override f1. The call to B1::f1 or B2::f2 will probably not require a table lookup because the implementation is specified explicitly (although it does still require the 'this' pointer fixup).
Баламалармен салыстыру
Виртуалды әдіс кестесі динамикалық жіберуді іске асыру үшін көбінесе тиімді шешім болып табылады, бірақ екілік ағаш жіберу сияқты, кейбір жағдайларда жоғары өнімділік беретін баламалар бар, бірақ олардың өзгеше ерекшеліктері де бар. Дегенмен, виртуалды әдіс кестелері арнайы "this" параметрі бойынша бір реттік жіберуді ғана қолдайды, ал көп реттік жіберу (CLOS, Dylan немесе Julia сияқты) барлық параметрлердің типтерін жіберу кезінде ескере алады. Виртуалды әдіс кестелері жіберу белгілі әдістер жиынтығымен шектелген жағдайда ғана жұмыс істейді, сондықтан оларды компиляция уақытында құрастырылған қарапайым массивке орналастыруға болады, бұл қаз түрлі жазу тілдерінен (Smalltalk, Python немесе JavaScript сияқты) өзгеше. Осы мүмкіндіктердің бірін немесе екеуін де ұсынатын тілдер көбінесе хэш-кестедегі жол іздеу немесе оған балама әдіс арқылы жібереді. Мұны жылдамдату үшін әртүрлі техникалар бар (мысалы, әдіс атауларын тіркеу/маркерлеу, іздеу нәтижелерін кэштеу, уақытында компиляция).
The virtual method table is generally a good performance trade off to achieve dynamic dispatch, but there are alternatives, such as binary tree dispatch, with higher performance in some typical cases, but different trade offs. However, virtual method tables only allow for single dispatch on the special "this" parameter, in contrast to multiple dispatch (as in CLOS, Dylan, or Julia), where the types of all parameters can be taken into account in dispatching. Virtual method tables also only work if dispatching is constrained to a known set of methods, so they can be placed in a simple array built at compile time, in contrast to duck typing languages (such as Smalltalk, Python or JavaScript). Languages that provide either or both of these features often dispatch by looking up a string in a hash table, or some other equivalent method. There are a variety of techniques to make this faster (e. g., interning/tokenizing method names, caching lookups, just in time compilation).