0988b24c

Вызов виртуального метода


Любой вызов виртуального метода требует косвенного вызова процедуры метода. Для этого используйте расширенную инструкцию CALL.METHOD. Для выполнения вызова Турбо Ассемблер генерирует следующие инструкции:

1. Загружает промежуточные регистры указателем на ТВМ из эк- земпляра объекта.

2. Выполняет косвенный вызов соответствующего элемента таб- лицы.

Таким образом, когда вы задаете:

CALL <экземпляр> METHOD <объект>:<метод> USES <seg>:<reg> <вызов_проц>

то генерируются следующие инструкции:

MOV <рег>, [<экземпляр>.<указатель_ТВМ>] CALL [(<сегм><рег>).<метод>] <вызыв_проц>

Первая инструкция загружает выбранный регистр <рег> адресом таблицы виртуальных методов из поля указателя ТВМ структуры объ- екта. Вторая инструкция выполняет косвенный вызов соответствующе- го метода в таблице.

Например, вызов в виде:

CALL es:di method list:insert uses ds:bx pascal,es di,es dx,es cx

генерирует последовательность вида:

mov bx,[es:di.@Mptr_list] CALL [ds:bx.insert] pascal,es di,es dx,es cx

Заметим, для объектов, описанных с таблицами NEAR, инструк- цией CALL.METHOD будет загружаться только регистр смещения. Сегментный регистр всегда должен содержать корректное значение. В следующем примере показано, как обеспечить правильную установку сегментного регистра:

; Добавить узел к концу объекта связанного списка. ; Это виртуальный метод "list_append". list_append PROC PASCAL NEAR ARG @@list:dword,\ @@new:dword USES dx,bx, es,di mov ax,@Data mov ds,ax les di,@@list sub ax,ax CALL es:di method list:insert uses DS:bx pascal, es di,@@new,ax ax ret ENDP

Примечание: Пока вы не инициализируете в данных объекта указатель таблицы виртуальных методов, ни один виртуальный метод вызвать нельзя. Это вызвано тем, что указатель загру- жает адрес ТВМ (из которой извлекается адрес нужной проце- дуры виртуального метода). Таким образом, если вы не иници- ализировали указатель на таблицу виртуальных методов, любой вызов виртуального метода приведет к вызову по некоторому случайному адресу.


В качестве другого примера рассмотрим базовый объект node (узел), который вы можете включить в любой объект, помещенный в связанный список или очередь.

node STRUC GLOBAL METHOD { construct:dword = node_construct ; подпрограмма ; конструктора узла destroy:dword = node_destroy ; подпрограмма ; деструктора узла init:dword = node_init ; подпрограмма ; инициализации узла deinit:dword = node_deinit ; подпрограмма ; деинициализации узла routine virtual next:word = node_adv ; подпрограмма ; следующего узла virtual prev:word = node_back ; подпрограмма ; предыдущего узла virtual print:word = node_print ; подпрограмма ; содержимого узла } node_next dd ? ; указатель следующего ; узла node_prev dd ? ; указатель ; предыдущего узла ends

Чтобы можно было использовать связанный список или очередь, вы можете определить любое число объектов, наследующих объект node. Приведем два примера:

mlabel STRUC GLOBAL node METHOD { virtual print:word = label_print } label_name db 80 dup (?) label_addr db 80*2 dup (?) label_city db 80 dup (?) label_state db 2 dup (?) label_zip db 10 dup (?) ENDS

book STRUC GLOBAL node METHOD { virtual print:word = book_print } book_title db 80 dup (?) book_author db 80 dup (?) ENDS

В следующем примере вы для объектов label и book вызываем методы путем вызова printit. Если "предком" является node, не важно, какой объект передается printit. Так как метод печати - это виртуальный метод, вызов выполняется косвенно через ТВМ объ- екта. При первом вызове printit, так как мы передаем экземпляр объекта label, вызывается процедура метода label_print. При вто- ром вызове printit вызывается процедура метода book_print, пос- кольку мы передаем экземпляр объекта book. Заметим, что если бы метод print был статическим, то при вызове node_print всегда вы- зывалась бы процедура node_print (что нежелательно).

call printit pascal,<<адрес экземпляра объекта label>> call printit pascal,<<адрес экземпляра объекта book>> . . . printit proc pascal near arg @@obj:dword uses ds,si,es,bx mov ax,@data mov es,ax lds si@@obj call ds:si method node:print uses es:bx pascal,ds si ret endp


Содержание раздела