Библиотека программиста

«Ни один ремесленник, который стремится к вершинам своей профессии, не примет негодных инструментов; и ни один производитель, который ценит качество работы, не будет упрашивать ремесленника принять их»

Главная страница > Язык Object Pascal > 25. Связывание методов

25. Связывание методов

(Английский термин – Method binding.)

Методы могут быть статическими (static – по умолчанию), виртуальными (virtual) или динамическими (dynamic). Виртуальные и динамические методы могут быть перекрытыми (override) и абстрактными (abstract). Эти спецификаторы (designators) методов играют роль тогда, когда объектом является класс-наследник. Они определяют, какая именно реализация метода будет вызвана в том или ином случае.

Статические методы (Static methods).

Методы являются статическими по умолчанию и слово static не указывается. Когда вызывается статический метод, для определения конкретной реализации метода используется информация времени компиляции. В следующих описаниях методы Draw являются статическими:

type

TFigure = class

procedure Draw;

end;

TRectangle = class(TFigure)

procedure Draw;

end;

С учетом этих описаний следующий код иллюстрирует вызов статических методов:

var

Figure: TFigure;

Rectangle: TRectangle;

begin

Figure := TFigure.Create;

Figure.Draw; // вызывается TFigure.Draw

Figure.Free;

Figure := TRectangle.Create;

Figure.Draw; // вызывается TFigure.Draw

TRectangle(Figure).Draw; // вызывается TRectangle.Draw

Figure. Free;

Rectangle := TRectangle.Create;

Rectangle.Draw; // вызывается TRectangle.Draw

Rectangle.Free;

end;

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

Для того чтобы сделать метод виртуальным или динамическим, надо добавить соответствующую директиву в его описание. Эти методы, в отличие от статических, могут быть перекрытыми (override) в классах наследниках.

Когда вызывается перекрытый метод, действительный, т.е. времени выполнения, тип класса или объекта, использованный в вызове метода (а не объявленный тип), определяет, какая реализация будет вызвана.

Для перекрытия метода его надо объявить с директивой override. Заголовок перекрывающего метода должен в точности соответствовать заголовку метода в родительском классе. В программе Method_Binding иллюстрируется специфика виртуальных и перекрытых методов.

Program Method_Binding;

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

{$APPTYPE CONSOLE} // консольное приложение

uses SysUtils;

type

TFigure = class

Procedure DrawV; virtual;

Procedure DrawO; virtual; {в производном классе этот метод будет объявлен

как перекрытый, но в родительском его надо объявлять просто виртуальным}

end;

TRectangle = class(TFigure)

Procedure DrawV; virtual; {[Warning] :

Method 'DrawV' hides virtual method of base type 'TFigure'

Это предупреждение можно убрать с помощью директивы reintroduce,

которая указывается вместо virtual}

Procedure DrawO; override;

end;

TEllipse = class(TRectangle)

Procedure DrawV; virtual;

Procedure DrawO; override;

end;

{ ----------------реализация TFigure----------------- }

procedure TFigure.DrawO;

begin

WriteLn('TFigure.DrawO');

end;

procedure TFigure.DrawV;

begin

WriteLn('TFigure.DrawV');

end;

{ ----------------реализация TRectangle ----------------}

procedure TRectangle.DrawO;

begin

WriteLn('TRectangle.DrawO');

end;

procedure TRectangle.DrawV;

begin

WriteLn('TRectangle.DrawV');

end;

{ ----------------реализация TEllipse ----------------}

procedure TEllipse.DrawO;

begin

WriteLn('TEllipse.DrawO');

end;

procedure TEllipse.DrawV;

begin

WriteLn('TEllipse.DrawV');

end;

{ -----------------------------------------------------------}

var

F : TFigure;

T : TRectangle;

BEGIN

F:=TRectangle.Create;

F.DrawV; //вызываетсяTFigure.DrawV

F.DrawO; //вызываетсяTRectangle.DrawO

F.Free;

F:=TEllipse.Create;

F.DrawV; // вызываетсяTFigure.DrawV

F.DrawO; // вызываетсяTEllipse.DrawO

TRectangle(F).DrawV; // вызываетсяTRectangle.DrawV

TRectangle(F).DrawO; // вызываетсяTEllipse.DrawO

F.Free;

T:=TRectangle.Create;

T.DrawV; // вызываетсяTRectangle.DrawV

T.Free;

ReadLn;

END.

Несмотря на то, что только виртуальные и динамические методы могут перекрываться, все методы могут быть перегружены (overload).

Виртуальность или динамичность? (Virtual versus dynamic).

Виртуальные и динамические методы являются семантически эквивалентными. Различие между этими методами проявляется только на этапе выполнения в способе реализации собственно вызова метода. Виртуальные методы оптимизированы по скорости вызова, в то время как динамические – по объему кода.

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

Перекрытие или прятание? (Overriding versus hiding)

Если объявление какого-либо метода имеет такой же заголовок, как и объявление наследуемого метода, и не имеет директивы override, новое объявление попросту "прячет" унаследованный метод без его перекрытия. Оба метода существуют в производном классе. Например:

type

T1 = class(TObject)

procedure Act; virtual;

end;

T2 = class(T1)

procedure Act; // Act переобъявлена, но не перекрыта

end;

var

SomeObject: T1;

begin

SomeObject := T2.Create;

SomeObject.Act; // вызывается T1.Act

T2(SomeObject).Act; // вызывается T2.Act

end;

Директива reintroduce.

Эта директива просто подавляет предупреждения компилятора о прятании прежде объявленного виртуального метода. Например

procedure DoSomething; reintroduce; { родительский класс также имеет метод DoSomething }

Абстрактные методы (Abstract methods).

Абстрактный метод – это виртуальный или динамический метод, который не имеет определяющего описания в том классе, где он объявлен. Его реализация откладывается до производного класса. Абстрактные методы должны объявляться с директивой abstract после virtual или dynamic. Например,

procedure DoSomething; virtual; abstract;

Абстрактный метод может быть вызван только в классе или объекте, в котором метод перекрыт, т.е. реализован.

Перегрузка методов (Overloading methods).

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

Непонятно, почему в Help Delphi6 советуют использовать директиву reintroduce в объявлении перегруженного метода в производном классе. Пример:

type

T1 = class(TObject)

procedure Test(I: Integer); overload; virtual;

end;

T2 = class(T1)

procedure Test(S: string); overload; virtual;

end;

SomeObject := T2.Create;

SomeObject.Test('Hello!'); // вызов T2.Test

SomeObject.Test(7); // вызов T1.Test

Функциональность классов, между прочим, никак не изменится, если убрать директиву virtual.





<< Предыдущая статья
«24. Поля и методы классов»
Следующая статья >>
26. Конструкторы и деструкторы