IMalloc =
interface(IUnknown)
['{00000002-0000-0000-C000-000000000046}']
function Alloc(Size: Integer): Pointer; stdcall;
functionRealloc(P:Pointer;Size:Integer):Pointer;stdcall;
procedure Free(P: Pointer); stdcall;
function GetSize(P: Pointer): Integer; stdcall;
function DidAlloc(P: Pointer): Integer; stdcall;
procedure HeapMinimize; stdcall;
end;
Перед использованием продекларированного интерфейса он должен быть реализован в классе. Реализация осуществляется с помощью декларации в списке прародителей класса:
type
className =
class (ancestorClass, interface1,...,interfaceN)
memberList
end;
Например,
type
TMemoryManager =
class(TInterfacedObject, IMalloc, IErrorInfo)
...
end;
Когда класс реализует интерфейс, он должен реализовать (или наследовать реализацию) каждого метода, декларированного в интерфейсе. Ниже приведено описание tInterfacedObject из модуля System:
type
TInterfacedObject =
class(TObject, IUnknown)
protected
FRefCount: Integer;
function QueryInterface(const IID: TGUID; out Obj): Integer; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
property RefCount: Integer read FRefCount;
end;
tInterfacedObject реализует интерфейс. Поэтому в нем описаны и реализованы все три метода IUnknown. Классы, реализующие интерфейсы, могут быть использованы как прародители. Класс tInterfacedObject реализует три метода интерфейса IUnknown и поэтому удобен как прародитель для всех классов, реализующих интерфейсы. Когда интерфейс реализован в классе, каждый из его методов реализован соответствующим методом (по умолчанию с тем же именем) и с такой же сигнатурой.
В классе-наследнике можно перекрыть методы реализуемого интерфейса. При этом соответствующий метод должен быть виртуальным или динамическим). Также возможно заново унаследовать интерфейс:
type
IWindow =
interface
['{00000115-0000-0000-C000-000000000146}']
procedure Draw;
...
end;
TWindow =
class(TInterfacedObject, IWindow) //TWindow реализует IWindow
procedure Draw;
...
end;
TFrameWindow =
class(TWindow, IWindow) //TFrameWindow переопределяет реализацию Iwindow
procedure Draw;
...
end;
При этом в классе все методы от прародительской реализации этого интерфейса запираются (не наследуются), в том числе выражения различения методов.
6.2. Реализация интерфейсов свойством
Директива implements в описании свойства класса позволяет делегировать реализацию интерфейса свойству в реализуемом классе. Например, свойство
property MyInterface: IMyInterface
read FMyInterface
implements IMyInterface;
описывает свойство, реализующее интерфейс ImyInterface. Директива implements должна быть последней в описании свойства и может иметь перечисление после нее имен нескольких интерфейсов, разделенных запятыми. Делегируемое свойство:
может иметь тип класса или интерфейса. не может быть массивом или иметь спецификатор индекса. должно иметь спецификатор read; если имеется метод “read”, он не может быть динамическим (хотя может быть виртуальным) или описывать директиву message. Если свойство типа интерфейс, соответствующий интерфейс или его интерфейс-прародитель должен присутствовать в списке интерфейсов класса, где продекларировано свойство.Делегируемое свойство должно возвращать объект, чей класс полностью реализует интерфейс, без использования выражений различия методов (“мэппинга”), см. далее. Например:
type
IMyInterface =
interface
procedure P1;
procedure P2;
end;
TMyClass =
class(TObject, IMyInterface)
FMyInterface: IMyInterface;
property MyInterface: IMyInterface
read FMyInterface
implements IMyInterface;
end;
tatherClass=
class(tany, ImyInterface)
…
end;
var
aMyClass: TMyClass;
aOtherClass:tOtherClass;
aMyInterface: IMyInterface;
begin
aMyClass := TMyClass. Create;
aMyClass. FMyInterface := ... // объект, чей класс реализует ImyInterface
aMyInterfase:=aMyClass. myInterfase// ссылка на тот же объект
aMyInterface := MyClass;// допустимо, так как ImyInteface – интерфейс-прародитель для tMyClass; ссылка на объект, агрегирующий
aMyInterface. MyInterface:=totherClass. create;//это второй объект
aMyInterface. P1;
aMyInterfase. MyInterfase. P1;//это вызов методаP1 для второго объекта
end;
6.3. Различение имен при реализации нескольких интерфейсов
Когда класс реализует два или более интерфейсов, имеющих методы с одинаковыми именами, для разрешения конфликта имен используют выражения различения методов в виде “мэппинга”:
procedure interface. interfaceMethod = implementingMethod;
или
function interface. interfaceMethod = implementingMethod;
где implementingMethod — это метод, определенный в классе, или одном из его прародителей. Он может быть методом, описанном далее в описании класса, но не может быть “private” методом класса или одного из его прародителей, описанном в другом модуле. Пример:
type
TMemoryManager = class(TInterfacedObject, IMalloc, IErrorInfo)
function IMalloc. Alloc = Allocate;
procedure IMalloc. Free = Deallocate;
...
end;
Выражения различения методов не могут использовать мэппинг, имеющийся в классах-прародителях.
В спецификации интерфейса может быть описан уникальный глобальный идентификатор — globally unique identifier (GUID), представленный в виде строки литералов, заключенной в скобки:
['{xxxxxxxx–xxxx–xxxx–xxxx–xxxxxxxxxxxx}']
где каждый X это 16-ричная цифра ( от 0 до F). GUID — это 16-байтовое двоичное число, уникально идентифицирующее интерфейс.
Если у интерфейса есть GUID, можно его использовать для запросов к переменной интерфейса, получить ссылки на его реализации. Типы tGUID и pGUID, определенные в модуле System, используются для работы с переменными типа GUID и указателями на такие переменные. Их описание следующее:
type
pGUID = ^tGUID;
tGUID =
record
D1: Longword;
D2: Word;
D3: Word;
D4: array[0..7] of Byte;
end;
Можно описать типизированную константу типа TGUID, например, имеющую имя IID - IMalloc:
const IID_IMalloc: TGUID = '{00000002-0000-0000-C000-000000000046}';
Вызов в процедуре переменной типа GUID аналогичен соответствующему типу interfase. Может быть использовано как значение, так и постоянный параметр типа TGUID. Например:
function Supports(Unknown: IUnknown; const IID: TGUID): Boolean;
Таким образом, вызов Supports может быть сделан одним из двух путей:
if Supports(Allocator, IMalloc) then...
или
if Supports(Allocator, IID_IMalloc) then...
5.11. Спецификаторы вызовов процедур и функций
При задании процедуры или функции можно использовать спецификаторы вызова register, pascal, cdecl, stdcall, safecall. Например:
function MyFunction(X, Y: Real): Real; cdecl;
Эти спецификаторы влияют на особенности передачи параметров в процедуру, а также на работу со стеком, использование регистров, и обработку исключительных ситуаций. По умолчанию спецификатор считается register.
Директива | Передача параметров | Очищает стек | Передает параметры | Вызовы |
Register | слева направо | программа | через регистры | по умолчанию, наиболее эфф. |
Pascal | слева направо | программа | через стек | обратно совместимо с Turbo PASCAL |
cdecl | справа налево | вызывающий | через стек | DLL, написанные на C или C++ |
stdcall | справа налево | программа | через стек | Windows API |
safecall | справа налево | программа | через стек | Объекты COM; методы парных интерфейсов |
5.12. Динамические массивы
Было:
var
A: array[1..100] of string;
B: array[1..10] of array [1..20] of integer;
Стало можно:
var
A: array of string;
B: array of array of integer;
Декларация динамического массива (переменных A и B) не выделяет под них памяти. Память выделяется процедурой SetLength:
SetLength(A,100);
SetLength(B,10,20);
Индексация динамических массивов всегда идет от нуля.
Для освобождения памяти из-под динамического массива:
а) либо присвоить nil переменной:
A:=nil;
B:=nil;
б) либо — вызвать процедуру Finalize:
Finalize(A);
Finalize(B);
Пример:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Label1: TLabel;
Label2: TLabel;
Button3: TButton;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Button4: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
A: array of string;
B: array of array of integer;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
close
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
SetLength(A,100);
SetLength(B,10,20);
Label1.caption:='Память выделена';
end;
procedure TForm1.Button3Click(Sender: TObject);
var s:string;
begin
A[99]:='Ok';
B[9,19]:=7;
Label3.caption:=A[99];
str(B[9,19],s);
Label5.caption:=s;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
A:=nil;
Finalize(B);
Label1.caption:='Память освобождена';
end;
end.
5.13. Перезагрузка (overloading) методов, процедур и функций
Разрешено использование одинаковых имен, но с разными сигнатурами аргументов (числом или типом аргументов):
Constructor create(aOwner:tComponent);overload;override;
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |


