Любой вызов виртуального метода требует косвенного вызова процедуры метода. Для этого используйте расширенную инструкцию CALL.METHOD. Для выполнения вызова Турбо Ассемблер генерирует следующие инструкции:
1. Загружает промежуточные регистры указателем на ТВМ из эк- земпляра объекта.
2. Выполняет косвенный вызов соответствующего элемента таб- лицы.
Первая инструкция загружает выбранный регистр <рег> адресом таблицы виртуальных методов из поля указателя ТВМ структуры объ- екта. Вторая инструкция выполняет косвенный вызов соответствующе- го метода в таблице.
Заметим, для объектов, описанных с таблицами 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. Приведем два примера:
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