Связь и интернет Архив Программирование
   
Сделать стартовойСделать закладку            
   ПОИСК  
   
Главная / Турбо Ассемблер / Использование Турбо Ассемблера /
8  Perl
8  PHP
8  JavaScript
8  HTML
8  DHTML
8  XML
8  CSS
8  C / C++
8  Pascal и Delphi
8  Турбо Ассемблер
8  MySQL
8  CASE-технологии
8  Алгоритмы
8  Python
8  Обратная связь
8  Гостевая книга
Новости о мире


Объектно-ориентированное программирование - Программирование от RIN.RU
Объектно-ориентированное программирование



Вызов метода объекта


Для вызова метода объекта используйте инструкцию CALL. Для вызова процедур методов Турбо Ассемблер обеспечивает расширение стандартной инструкции CALL - CALL.METHOD.


Примечание: Синтаксис инструкции CALL для вызова статических и виртуальных методов совпадает.


Вызов статического метода


При вызове процедуры метода, даже если вы знаете, что вызывается статический метод, следует записывать инструкцию CALL.METHOD, как если бы вы вызывали виртуальный метод. Это позволит при вызове статических методов избежать нежелательных эффектов и предоставляет вам гибкость - возможность изменять статические методы на виртуальные и обратно без необходимости изменения вызова метода. По этой же причине имеет смысл выбирать промежуточные регистры вызова, даже если вы знаете, что вызываемый вами метод является статическим.


Вызовы статических методов определяются на этапе компиляции и преобразуются в непосредственный вызов нужной процедуры метода объекта. Однако при выполнении вызова не следует вызывать процедуру метода непосредственно. Вместо этого используйте расширенную инструкцию CALL.METHOD.


В следующей таблице показан пример вызова статического метода init для объекта связанного списка:


CALL foolist METHOD list:init pascal,ds offset foolist
CALL es:di METHOD list:init pascal,es di


Сам адрес вызова является адресом экземпляра объекта. Этот адрес используется только по синтаксическим причинам. Фактически генерируемым адресом является непосредственный вызов процедуры метода.


В данном примере первым вызовом является вызов метода init объекта list. Так как это статический метод, вы выполняете непосредственный вызов процедуры метода list_init. Турбо Ассемблер игнорирует экземпляр объекта foolist (он только передается в качестве аргумента процедуре метода).


За именем вызова следует обычный расширенный параметр языка и список параметров. Язык и параметры зависят от вызываемого вами метода. Один из параметров обычно является указателем на экземпляр объекта. В данном примере метод воспринимает один параметр, являющийся указателем на экземпляр объекта.


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


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


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

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




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


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


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


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




Вызов виртуальных методов "предков"


Благодаря тому, что вы повторно сможете использовать исходный код, применение виртуальных методов "предков" может помочь вам записывать методы порожденных классов. Например, пока вы не зададите, является элемент очередью или списком, для очереди можно использовать те же методы вывода, что и для списка. В классе списка вы можете записать:


virtual show:word = list_show


а в классе очереди:


virtual show:word = queue_show


Подпрограмма list_show может печатать LIST SHOW: с последующим выводом отдельных элементов списка. Однако в порожденном классе, если queue_show использует подпрограмму печати, она должна печатать собственный заголовок QUEUE SHOW: и использовать list_show только как механизм последовательного прохода по списку и печати отдельных элементов. list_show может определить передаваемый ей тип структуры и в зависимости от этого печатать заголовок списка. Если подпрограмма для list_show посмотрит на указатель таблицы виртуальных методов передаваемой ей структуры, она сможет определить, совпадает ли указатель с указателем, установленным в подпрограмме list_init для списков (или они различны). Если указатель ТВМ в структуре не указывает на таблицу виртуальных методов для списков, то вероятно структура является порожденным типом. list_show может выполнить эту проверку с помощью следующих операторов:


cmp [([es:di]).@mptr_list],offset @TableAddr_LIST
jne @@not_a_list ; пропустить печать заголовка списка


; Если мы попали сюда, то это список, и следует
; распечатать его заголовок.
.
@@not_a_list:
; Теперь вывести отдельные элементы списка.




Как можно вызвать класс списка и метод вывода из подпрограммы queue_show? Если бы вы вызвали list_show непосредственно, то в подпрограмме могла бы возникнуть проблема, если имя используемого для вывода метода изменилось. (Вы можете не помнить об изменениях в вызове queue_show.) Если в queue_show вы поместите следующий оператор:


call(es:di) method list:show


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


Наилучшим способом вызова метода класса является следующий:


call +@table_list | show


Поскольку при описании класса list_show было задано как значение элемента вывода @table_list, Турбо Ассемблер автоматически транслирует данный оператор в непосредственный вызов list_show. Заметим, что хотя в списке метод вывода описывается как виртуальный, задание вызова приводит к тому, что Турбо Ассемблер выполняет непосредственный вызов без просмотра ТВМ.


Примечание: Виртуальные подпрограммы обычно вызываются косвенным образом через просмотр ТВМ.


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


mov bx,offset @TABLEADDR_LIST
call [(@table_list ptr es:bx).SHOW]


Это аналогично последовательности инструкций, которые Турбо Ассемблер использует для выполнения косвенного вызова через ТВМ.




Кое-что еще о вызове методов


Часто может встречаться ситуация, когда необходимо вызвать метод порождающего объекта из процедуры метода порожденного объекта. Для этого также можно использовать оператор CALL.METHOD.


Аналогично инструкции CALL.METHOD вы можете использовать расширение инструкции JMP с ключевым словом METHOD. Эта инструкция обеспечивает оптимальную рекурсию. См. здесь, где об инструкциях CALL.METHOD и JMP.METHOD рассказывается подробнее.


<<<  НазадВперед  >>>
 1  2  3  4  5  6 


 8  Комментарии к статье  8 8  Обсудить в чате

8  В тему

Начало работы на Турбо Ассемблере

Использование директив и параметров

Общие принципы программирования

Использование выражений и значений идентификаторов

Директивы выбора процессора

Использование моделей памяти программы и сегментации

Определение типов данных

Задание и использование счетчика адреса

Описание процедур

Управление областью действия идентификаторов

Определение данных

Расширенные инструкции

Использование макрокоманд

 
  
  
    Copyright ©  RIN 2003 - 2004      * Обратная связь