Методы Мб виртуальными,а могут быть статическими 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Методы Мб виртуальными,а могут быть статическими



Виртуальные и динамические методы

Статические методы удобны, когда в программе заранее известно, какие типы

объектов будут использоваться, и приведения типов, как правило, не требуется.

Б ряде случаев, особенно в крупных проектах, часто приходится хранить в перемен-

ных объекты-наследники (как в примере с ТСаг и TMAZ), причем конкретный тип

этих объектов может быть неизвестен. Поэтому в Паскале реализован новый тип

методов — виртуальные методы (для их описания существует зарезервированное

слово virtual). Описать виртуальный метод можно так.

(??мин 42)В случае вёрчиал извлекается по индексам бит таблица адреса вируальной процедуры и по этому адресу

вёрчиал позволяет вызывать быстрее,но зато таблицы виртуальных методов сильно распухают.те как только мы какому-нибудь классу решили добавить виртуальный метод ради того,что бы у одного потомка его переопределить,но у этого класса есть 20 потомков,то у всех 20ти потомков в соответствующих виртуальных методах появится по новому элементу.а если мы используем дайнэмик,то у нас просто в двух таблицах у базового класса добавится один элемент и вот у этого…???...появится по одному элементу.во всех остальных вообще ничего не изменится

type TCar = class(TObect)

procedure Move; virtual;

end;

Такие методы в классах-наследниках могут быть перекрыты методами с одноимен-

ными заголовками. Чтобы явно указать компилятору, что определенный метод пере-

крывает виртуальный метод родителя, надо использовать ключевое слово override.

type TMAZ = class<TCar>

procedure Move; override;

end;

Теперь в приведенном примере можно не выполнять приведение типов. Во время

работы программы она самостоятельно определит тип хранимого в переменной

Саг объекта и вызовет нужный метод.

Car:= TMAZ.Create;

Car. Move,-

Вызовется метод Move класса TMAZ. Если же этот метод не перекрыть (не указать

ключевое слово override), то вызовется метод Move класса Саг.

Вместо ключевого слова virtual можно применять ключевое слово dynamic, выпол-

няющее тс же функции и описывающее метод как динамический.

Классы и объекты 93

type TCar = class(TObectj

procedure Move; dynamic;

end;

Разница между виртуальными и динамическими методами заключается только в

деталях программной реализации. Виртуальные методы оптимизированы для макси-

мального быстродействия, динамические — для максимальной экономии памяти

(создания компактного кода).__

Абстрактные методы

В некоторых случаях не имеет смысла выполнять реализацию определенных методов

базового класса, например, когда все реализации некоторого метода сильно отлича-

ются друг от друга, а метод родительского класса не используется. Вместе с тем соответ-

ствующий метод обязан быть реализован в каждом из классов-наследников.

Такой метод надо объявить в родительском классе как абстрактный.

type TCar = class(TObect)

procedure Move; virtual; abstract;

end;

Теперь описывать реализацию метода TCar.Move не надо.

ЗАМЕЧАНИЕ Абстрактным может быть только динамический или виртуальный

метод.

Понятие виртуального метода

Все методы, которые до сих пор рассматривались, имеют одну общую черту — все они статические. При обращении к статическому методу компилятор точно знает класс, которому данный метод принадлежит. Поэтому, например, обращение к статическому методу ParseLine в методе NextLine (принадлежащем классу TTextReader) компилируется в вызов TTextReader.ParseLine:

function TTextReader.NextLine: Boolean;var S: string; N: Integer;begin Result:= not EndOfFile; if Result then begin Readln(FFile, S); N:= ParseLine(S); // Компилируется в вызов TTextReader.ParseLine(S); if N <> ItemCount then SetLength(FItems, N); end;end;

 

В результате метод NextLine работает неправильно в наследниках класса TTextReader, так как внутри него вызов перекрытого метода ParseLine не происходит. Конечно, в классах TDelimitedReader и TFixedReader можно продублировать все методы и свойства, которые прямо или косвенно вызывают ParseLine, но при этом теряются преимущества наследования, и мы возвращаемся к тому, что необходимо описать два класса, в которых большая часть кода идентична. ООП предлагает изящное решение этой проблемы — метод ParseLine всего-навсего объявляется виртуальным:

type TTextReader = class... function ParseLine(const Line: string): Integer; virtual; //Виртуальный метод... end;

 

Объявление виртуального метода в базовом классе выполняется с помощью ключевого слова virtual, а его перекрытие в производных классах — с помощью ключевого слова override. Перекрытый метод должен иметь точно такой же формат (список параметров, а для функций еще и тип возвращаемого значения), что и перекрываемый:

type TDelimitedReader = class(TTextReader)... function ParseLine(const Line: string): Integer; override;... end; TFixedReader = class(TTextReader)... function ParseLine(const Line: string): Integer; override;... end;

 

Суть виртуальных методов в том, что они вызываются по фактическому типу экземпляра, а не по формальному типу, записанному в программе. Поэтому после сделанных изменений метод NextLine будет работать так, как ожидает программист:

function TTextReader.NextLine: Boolean;var S: string; N: Integer;begin Result:= not EndOfFile; if Result then begin Readln(FFile, S); N:= ParseLine(S); // Работает как <фактический класс>.ParseLine(S) if N <> ItemCount then SetLength(FItems, N); end;end;

 

Работа виртуальных методов основана на механизме позднего связывания (late binding). В отличие от раннего связывания (early binding), характерного для статических методов, позднее связывание основано на вычислении адреса вызываемого метода при выполнении программы. Адрес метода вычисляется по хранящемуся в каждом объекте описателю класса.

Благодаря механизму наследования и виртуальных методов в среде Delphi реализуется такая концепция ООП как полиморфизм. Полиморфизм существенно облегчает труд программиста, поскольку обеспечивает повторное использование кода уже написанных и отлаженных методов.

Механизм вызова виртуальных методов

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

Все процедурные переменные с адресами виртуальных методов пронумерованы и хранятся в таблице, называемой таблицей виртуальных методов (VMT — от англ. Virtual Method Table). Такая таблица создается одна для каждого класса объектов, и все объекты этого класса хранят на нее ссылку.

Структуру объекта в оперативной памяти поясняет рисунок 3.3:


Рисунок 3.3. Структура объекта TTextReader в оперативной памяти

Вызов виртуального метода осуществляется следующим образом:

  1. Через объектную переменную выполняется обращение к занятому объектом блоку памяти;
  2. Далее из этого блока извлекается адрес таблицы виртуальных методов (он записан в четырех первых байтах);
  3. На основании порядкового номера виртуального метода извлекается адрес соответствующей подпрограммы;
  4. Вызывается код, находящийся по этому адресу.

Покажем, как можно реализовать косвенный вызов виртуального метода ParseLine (он имеет нулевой номер в таблице виртуальных методов) обычными средствами процедурного программирования:

type TVMT = array[0..9999] of Pointer; TParseLineFunc = function (Self: TTextReader; const Line: string): Integer;var Reader: TTextReader; // объектная переменна ObjectDataPtr: Pointer; // указатель на занимаемый объектом блок памяти VMTPtr: ^TVMT; // указатель на таблицу виртуальных методов MethodPtr: Pointer; // указатель на методbegin... ObjectDataPtr:= Pointer(Reader); // 1) обращение к данным объекта VMTPtr:= Pointer(ObjectDataPtr^); // 2) извлечение адреса VMT MethodPtr:= VMTPtr^[0]; // 3) извлечение адреса метода из VMT TParseLineFunc(MethodPtr)(Reader, S); // 4) вызов метода...end.

 

Поддержка механизма вызова виртуальных методов на уровне языка Delphi избавляет программиста от всей этой сложности.



Поделиться:


Последнее изменение этой страницы: 2017-02-21; просмотров: 211; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.119.104.238 (0.011 с.)