Overload — Директива Delphi


Содержание

Перегрузка методов в Delphi

Есть еще одна, совершенно особенная разновидность методов — перегружаемые.

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

Рассмотрим немного измененный пример, иллюстрирующий статические методы:

procedure SetData(AValue: Extended);

end; T2ndObj = class(TlstObj)

procedure SetData(AValue: Integer); end;

В этом случае попытка вызова из объекта Т2 методов

вызовет ошибку компиляции на первой из двух строк. Для компилятора внутри Т2 статический метод с параметром типа extended перекрыт, и он его «не признает». Где же выход из сложившегося положения? Переименовать один из методов, например создать SetlntegerData и SetExtendedData ? Можно, но если методов не два, а, скажем, сто, моментально возникнет путаница. Сделать методы виртуальными? Нельзя, поскольку тип и количество параметров в одноименных виртуальных методах должны в точности совпадать. Теперь для этого существуют перегружаемые методы, объявляемые при помощи директивы overload :

procedure SetData(AValue: Extended);overload;

procedure SetData(AValue: Integer); overload;

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

Можно перегрузить и виртуальный (динамический) метод. Надо только в этом случае добавить директиву reintroduce :

procedure SetData(AValue: Extended); overload; virtual;

procedure SetData(AValue: Integer); reintroduce; overload;

На перегрузку методов накладывается ограничение — нельзя перегружать методы, находящиеся в области видимости published , т. е. те, которые будут использоваться в Инспекторе объектов.

Overload — Директива Delphi

This is useful when there are a number of ways that code may want to use the routine. For example, if the routine is a class constructor, you may want one version of Create that sets default values, and another that takes these values as parameters.

You must code the Overload directive before any other directives.

When calling an overloaded routine, Delphi chooses the appropriate version based first on number of arguments, then on the argument types. If it cannot make a decision, it throws an exception.

When the argument count is the same, it always tries to satisfy the simplest/smallest data types first — for example, the above value 23 would satisfy a Byte argument ahead of an Integer argument.

Sometimes, you can avoid the need for overloading by giving final arguments default values. The caller can then call with or without these final parameters.

procedure MyProc(a : Byte; b : Byte = 23);

can be called in two ways:

MyProc(15, 16);
MyProc(45); // Defaults b to value 23

Overload — Директива Delphi

от ведь как. 10 минут думал, чего компилятору не нравится. ищет, видимо, перегрузку с параметром Pointer.
век живи . как говорится.

Type
TMethod=Procedure of Object;
T > Private
Procedure lcMethod;
Public
Constructor Create;
End;

Procedure Proc(Const s:String;m:TMethod); Overload;
Begin
End;

Procedure Proc(n:Integer;m:TMethod); Overload;
Begin
End;

Constructor TClass1.Create;
Begin
Proc(1,Nil);
End;

D6, не компилируется

кто может проверить на других версиях, если есть желание — отпишитесь

> кто может проверить на других версиях, если есть желание
> — отпишитесь

Constructor TClass1.Create;
Begin
Proc(1,Nil);

There is no overloaded version of «Proc» that can be called with these arguments

Если поменять:
TMethod=Procedure of Object;
на
TMethod=Procedure;

Всё нормально компилиться.
D5


> Игорь Шевченко © (15.04.08 13:51) [4]

так это понятно :)
другое интересно: неявное приведение не реализовали при поиске перегруженной


> AlexKniga © (15.04.08 13:50) [3]

метод и процедура это разные вещи

Constructor TClass1.Create;
Begin
Proc(1,TMethod(Nil));
End;


> Игорь Шевченко © (15.04.08 13:51) [4]
> Оригинал (15.04.08 13:58) [6]

я не совета просил, мне ситуация с компиляторами интересна


> Palladin © (15.04.08 14:00) [7]
>
> > Игорь Шевченко © (15.04.08 13:51) [4]
> > Оригинал (15.04.08 13:58) [6]
>
> я не совета просил, мне ситуация с компиляторами интересна


А разве компилятор не прав?

Тоже самое и в RAD 2007.


> другое интересно

наверное из-за этого
Procedure Proc(n:Integer;m:TMethod1); Overload;
Begin
End;

Procedure Proc(n:Integer;m:TMethod2); Overload;
Begin
End;

А FPC (Lazarus) схавал.


> Оригинал (15.04.08 14:03) [9]

TMethod=Procedure of Object;
Procedure Proc(n:Integer;m:TMethod);

.
Proc(1,Nil); // неявное приведение к TMethod
.

так где компилятор неправ? в [0] или тут?


> Proc(1,Nil); // неявное приведение к TMethod

С какой радости ?


> Игорь Шевченко © (15.04.08 14:07) [14]

с такой, что компилируется

TMethod смотрим объявление:

TMethod = record
Code, Data: Pointer;
end;

В честь чего компилятор неявно должен nil к TMehtod приводить ?


> с такой, что компилируется

если overload нету ?


> Игорь Шевченко © (15.04.08 14:08) [17]

а с какой стати ты его явно приводишь в [4]?

Palladin © (15.04.08 14:11) [20]


> Игорь Шевченко © (15.04.08 13:51) [4]
>
> Proc(1,TMethod(Nil));
>
> D2006


> Palladin © (15.04.08 13:57) [5]
> > Игорь Шевченко © (15.04.08 13:51) [4]
>
> так это понятно :)

А в D7 так тоже не компилируется. Invalid typecast


> Bless © (15.04.08 16:58) [22]

не обращай внимания. я не вдумался чего там Игорь написал.

Использование процедур и функций в Delphi

Скобки

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

Возможность перегрузки

Впервые концепция перегрузки процедур и функций была реализована в Delphi 4. Она позволяет иметь несколько различных процедур и функций с одинаковыми именами, но с разными списками параметров. Такие процедуры и функции должны быть описаны с применением директивы overload.

procedure Test (I: integer); overload;
procedure Test (S: string); overload;
procedure Test (D: double); overload;

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

Передача параметров

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

Передача параметров по значению

Этот режим передачи параметров применяется по умолчанию. Если параметр передается по значению, создается локальная копия данной переменной, которая и предоставляется для обработки в процедуру или функцию. Посмотрите на следующий пример:

procedure Test(s: string);

При вызове указанной процедуры будет создана копия передаваемой ей в качестве параметра строки s, с которой и будет работать процедура Test. При этом все внесенные в строку изменения никак не отразятся на исходной переменной s.

Однако это не относится к объектам. Например, если в функцию передается переменная (а точнее экземпляр объекта) TStringList, то в данном случае произойдет передача по ссылке (даже если это не указано явно). Этот способ передачи является у большинства самым излюбленным, но в тоже время является и самым не практичным, т.к. для выполнения метода выделяется дополнительная память для создания точной копией передаваемой переменой. Для решения этой проблемы следует использовать один из способов описанных ниже.

Передача параметров по ссылке

Pascal позволяет также передавать параметры в функции или процедуры по ссылке — такие параметры называются параметрами-переменными. Передача параметра по ссылке означает, что функция или процедура сможет изменить полученные значения параметров. Для передачи параметров по ссылке используется ключевое слово var, помещаемое в список параметров вызываемой процедуры или функции.

procedure ChangeMe(var x: longint);
begin
x := 2; // Параметр х изменен вызванной процедурой
end;

Вместо создания копии переменной x, ключевое слово var требует передачи адреса самой переменной x, что позволяет процедуре непосредственно изменять ее значение.

Передача параметров констант

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


procedure Test(const s: string );

Передача открытых массивов

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

function AddEmUp(A: array of integer): integer;

В открытом массиве можно передавать переменные, константы или выражения из констант.

Для получения информации о фактически передаваемом массиве параметров в функции или процедуре могут использоваться функции High, Low и SizeOf.

Object Pascal также поддерживает тип array of const, который позволяет передавать в одном массиве данные различных типов. Синтаксис объявления функций или процедур, использующих такой массив для получения параметров, следующий:

procedure WhatHaveIGot( A: array of const );

Вызвать объявленную выше функцию можно, например, с помощью такого оператора:

procedure WhatHaveIGot( [‘Text’, 10, 5.5, @WhatHaveIGot, 3.14, true, ‘c’] );

При передаче функции или процедуре массива констант все передаваемые параметры компилятор неявно конвертирует в тип TVarRec. Тип данных TVarRec объявлен в модуле System следующим образом:

PVarRec = ^TVarRec;
TVarRec = record
case Byte of
vtInteger: (VInteger: Integer; VType: Byte);
vtBoolean: (VBoolean: Boolean);
vtChar: (VChar: Char);
vtExtended: (VExtended: PExtended);
vtString: (VString: PShortString);
vtPointer: (VPointer: Pointer);
vtPChar: (VPChar: PChar);
vtObject: (VObject: TObject);
vtClass: (VClass: TClass);
vtWideChar: (VWideChar: WideChar);
vtPWideChar: (VPWideChar: PWideChar);
vtAnsiString: (VAnsiString: Pointer);
vtCurrency: (VCurrency: PCurrency);
vtVariant: (VVariant: PVariant);
vtInterface: (VInterface: Pointer);
vtWideString: (VWideString: Pointer);
vtInt64: (VInt64: PInt64);
end;

Поле VType определяет тип содержащихся в данном экземпляре записи TVarRec данных и может принимать одно приведенных значений.

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

procedure WhatHaveIGot( A: array of const );
var
i: integer;
TypeStr: string;
begin
for i := Low(A) to High(A) do
begin
case A[i].VType of
vtInteger : TypeStr := ‘Integer’;
vtBoolean : TypeStr := ‘Boolean’;
vtChar : TypeStr := ‘Char’;
vtExtended : TypeStr := ‘Extended’;
vtString : TypeStr := ‘String’;
vtPointer : TypeStr := ‘Pointer’;
vtPChar : TypeStr := ‘PChar’;
vtObject : TypeStr := ‘Object’;
vt ;
vtW ;
vtPW ;
vtAnsiString : TypeStr := ‘AnsiString’;
vtCurrency : TypeStr := ‘Currency’;
vtVariant : TypeStr := ‘Variant’;
vtInterface : TypeStr := ‘Interface’;
vtW ;
vtInt64 : TypeStr := ‘Int64’;
end;
ShowMessage( Format( ‘Array item %d is a %s’, [i, TypeStr] ) );
end;
end;

Значения параметров по умолчанию

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

procedure HasDefVal( s: string; i: integer = 0 );

Подобное объявление означает, что процедура HasDefVal может быть вызвана двумя путями. В первом случае — как обычно, с указанием обоих параметров:

procedure HasDefVal( ‘Hello’, 26 );

Во втором случае можно задать только значение параметра s, а для параметра i использовать значение, установленное по умолчанию:

procedure HasDefVal( ‘Hello’ );

При использовании значении параметров по умолчанию следует помнить о нескольких приведенных ниже правилах:

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

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

function Add( I1, I2: integer ): integer;
begin
Result := I1 + I2;
end;

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

function Add( I1, I2: integer; I3: integer = 0 ): integer;
begin
Result := I1 + I2 + I3;
end;

Директива

Директива <$X->запрещает вызов функций как процедур (с игнорированием возвращаемого результата). По умолчанию этот режим включен (<$X+>). Так вот, запомните, использование переменной Result недопустимо при сброшенном флажке опции Extended Syntax, расположенном во вкладке Compiler диалогового окна Project Options, или при указании директивы компилятора <$X->.

В каждой функции языка Objecl Pascal существует локальная переменная с именем Result, предназначенная для размещения возвращаемого значения. Кроме того, вернуть значение из функции можно также путем присвоения значения переменной, имеющей то же имя, что и данная функция. Это стандартный синтаксис языка Pascal, сохранившийся от его предыдущих версий. При использовании в теле функции переменной с ее именем не забывайте, что существуют большие отличия в обработке этого имени — все зависит от того, где она расположена — в левой части оператора присвоения или же в любом другом месте текста функции. Если имя функции указано в левой части оператора присвоения, то предполагается, что назначается возвращаемое функцией значение. Во всех других случаях предполагается, что осуществляется рекурсивный вызов этой функции.

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

Delphi in a Nutshell by Ray Lischner

Stay ahead with the world’s most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to v > Start Free Trial

No credit card required

Syntax

Description

The overload directive tells Delphi that you will declare another subroutine with the same name but with different parameters.

Tips and Tricks

If you use the overload directive in a method declaration, it must appear before the virtual , dynamic , or abstract directives.

You can declare any function, procedure, or method with the overload directive.

You can overload a method in a derived >overload directive.


An alternative to using overloaded methods is to use default parameters. For parameters with simple types, default parameters usually result in less code to write and maintain. For complex parameters, such as objects, you might find it easier to write overloaded subroutines that call each other.

The compiler uses the type and number of the actual arguments to determine which overloaded routine to call. To distinguish between different integer types, it uses the narrowest type possible. If the compiler cannot decide which overloaded routine to call, it issues an error.

Перегрузка процедур в Delphi

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;

type
TForm1 = class(TForm)
procedure dk(k:integer); overload;
procedure dk(k:real);
private
< Private declarations >
public
< Public declarations >
end;

var
Form1: TForm1;

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;

type
TForm1 = class(TForm)
procedure dk(k:integer); overload;
procedure dk(k:real); overload;
private
< Private declarations >
public
< Public declarations >
end;

Перегрузка методов в Delphi

Есть еще одна, совершенно особенная разновидность методов — перегружаемые.

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

Рассмотрим немного измененный пример, иллюстрирующий статические методы:

procedure SetData(AValue: Extended);

end; T2ndObj = class(TlstObj)

procedure SetData(AValue: Integer); end;

В этом случае попытка вызова из объекта Т2 методов

вызовет ошибку компиляции на первой из двух строк. Для компилятора внутри Т2 статический метод с параметром типа extended перекрыт, и он его «не признает». Где же выход из сложившегося положения? Переименовать один из методов, например создать SetlntegerData и SetExtendedData ? Можно, но если методов не два, а, скажем, сто, моментально возникнет путаница. Сделать методы виртуальными? Нельзя, поскольку тип и количество параметров в одноименных виртуальных методах должны в точности совпадать. Теперь для этого существуют перегружаемые методы, объявляемые при помощи директивы overload :

procedure SetData(AValue: Extended);overload;

procedure SetData(AValue: Integer); overload;

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

Можно перегрузить и виртуальный (динамический) метод. Надо только в этом случае добавить директиву reintroduce :

procedure SetData(AValue: Extended); overload; virtual;

procedure SetData(AValue: Integer); reintroduce; overload;

На перегрузку методов накладывается ограничение — нельзя перегружать методы, находящиеся в области видимости published , т. е. те, которые будут использоваться в Инспекторе объектов.

Режимы компиляции

18.04.2005
Иван Шихалев

Режимы компиляции, или иначе — режимы совместимости, в Free Pascal определяют, каким диалектом языка Pascal вы хотите вос­поль­зо­вать­ся. Режим определяет возможные конструкции языка, некоторые типы данных и системные модули подгружаемые по умол­ча­нию.

Режим компиляции выставляется при помощи ключа командной строки -S или непосредственно в модуле при помощи директивы <$MODE xxx>.

В этом же обзоре я намерен описать прочие ключи компилятора, определяющие расширения языка.

Режим по умолчанию. Соответственно в ключах командной строки своего символа не имеет. Что, впрочем, не есть хорошо, по­сколь­ку существует же еще и файл ppc386.cfg — его установки таким образом мы можем сменить на FPC только через директиву.

При присваивании значения процедурной переменной вы можете использовать как @

. Что са­мое интересное — это распространяется и на функции. Выражение @

однозначно трактуется как адрес переменной, а не процедуры, на которую она ссылается. Кстати, в справке по этому поводу ошибка, или я что-то не так понял: «You must use the address operator to assign procedural variables.»

Заголовок предварительно объявленной (в интерфейсной части модуля или при помощи директивы forward ) функции или про­це­ду­ры должен полностью совпадать с ее заголовком при реализации. То есть опускать список аргументов и тип результата — не­ль­зя.

Разрешена перегрузка (overloading) процедур и функций. Кстати, в отличие от Delphi, директиву overload использовать не­обя­за­тель­но.

Разрешены вложенные комментарии, таким образом, конструкция < comment < nested comment >> не вызовет ошибки ком­пи­ля­то­ра.

Не поддерживаются классы. Из-за того, что обработка исключений реализована a-la Delphi, не поддерживается оператор try .

Не подгружается модуль ObjPas . Что это за зверь, я слегка опишу, когда перейдем к <$MODE OBJFPC>, пока лишь замечу, что тип integer без ObjPas соответствует 2м байтам, а с ним — 4м.

Не поддерживается ключевое слово resourcestring .

До кучи ко всем огорчениям, не поддерживается псевдо(?)переменная result — результат функции присваивается ее имени.

Зато поддерживается директива cvar и внешние переменные.

Автоматически производится присваивание PChar -> string .


Поддерживается такая замечательная вещь, как перегрузка операторов.

Режим совместимости с Turbo Pascal фирмы Borland версии 7. Соответствует ключу командной строки -So .

Совместимость обеспечивается полная — единственное различие в том, что исполняемый файл 32-разрядный, других Free Pascal не создает.

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

В силу полной совместимости, воздержусь от подробного описания этого режима — чего-чего, а уж литературы по Turbo Pascal до­ста­точ­но, и в Сети, и в книжных магазинах.

По идее — режим совместимости с GPC — GNU Pascal. Ключ командной строки — -Sp .

Судя по всему — разработка данного режима для команды FPC — мягко говоря, задача не приоритетная. Реально никаких свой­ствен­ных GPC расширений не поддерживается, и от <$MODE TP>этот режим отличается только использованием адресного опе­ра­то­ра для процедурных переменных.

Это — режим Object Pascal. Соответствует ключу командной строки -S2 .

Работа с процедурными переменными полностью аналогична режиму <$MODE FPC>. Аналогично с заголовками функций и про­це­дур — они должны повторяться в точности при реализации. Вообще, режимы очень похожи, главное отличие — поддержка клас­сов.

Классы почти полностью идентичны классам в Delphi. Естественно, published практически эквивалентно public . Кроме того, пе­ре­груз­ка методов не требует директивы overload . Обидно, что не поддерживаются интерфейсы, но авторы обещают сделать это к версии 1.2.

Автоматически подгружается модуль ObjPas . Данный модуль по сути дополняет функциональность модуля System с уровня Turbo Pascal до уровня Delphi. Хотя, конечно, это грубое упрощение. Что реально он делает?

  • Во-первых, определяет тип smallint — целое со знаком длиной в слово, и переопределяет тип integer — в отличие от других режимов он означает целое со знаком размером в двойное слово, что соответствует типу longint , который остается таким же.
  • Во-вторых, для совместимости с Delphi определены процедуры AssignFile и CloseFile .
  • В-третих, переопределяет процедуры GetMem и FreeMem . К сожалению, я пока не ковырялся в исходниках и не могу сказать, что именно в их реализации изменено.
  • И в-четвертых, в этом модуле определяются функции для работы с ресурсными строками.

Free Pascal при <$MODE OBJFPC>поддерживает работу с ресурсными строками, через resourcestring .

При написании функций, для возвращаемого значения можно использовать псевдопеременную result . Что на мой взгляд гораздо удоб­нее, чем имя функции, особенно при итерациях.

Преобразование PChar -> string производится при присваивании автоматически.

Второе (после классов) по важности преимущество данного режима — обработка исключений в блоках try . except . end и try . finally . end . Данный механизм Free Pascal поддерживает полностью аналогично Delphi — то есть вы можете пе­ре­хва­ты­вать исключения, обрабатывать в зависимости от класса с помощью ключевого слова on , определять собственные классы ис­клю­че­ний и вызывать их (или стандартные) посредством оператора raise . Для корректной работы с исключениями нужно под­клю­чать, как и в Delphi, модуль SysUtils , который содержит базовый класс Exception и стандартные классы исключений.

В общем и целом данный режим очень похож на Object Pascal a-la Borland Delphi. Разработчики намерены реализовать и ин­тер­фей­сы к версии 1.2, но когда она выйдет, пока неизвестно.

Режим совместимости с Borland Delphi версии 2, если я не ошибаюсь. Соответствует ключу командной строки -Sd .

От <$MODE OBJFPC>отличается весьма незначительно:

  • Во-первых, необязательно в секции implementation повторять заголовок процедуры или функции.
  • И, во-вторых, не разрешена перегрузка функций, процедур и операторов.

В целом, я бы рекомендовал все-таки <$MODE OBJFPC>, который соединяет в себе объектную модель Object Pascal со всеми пре­лес­тя­ми собственно FPC.

Прочие расширения языка

Поддержка inline- процедур, функций и операторов. Определяется директивой компилятора <$INLINE ON/OFF>и ключом ко­манд­ной строки -Si . Inline-процедуры описываются директивой inline в заголовке, могут быть как нормальными пас­ка­лев­ски­ми, так и ассемблерными (при использовании директивы assembler ), то есть не требуют знания машинных кодов, как директива inline в Turbo Pascal. При использовании этой возможности следует помнить, что в случае ошибки (рекурсия, etc.) компилятор по­че­му-то не выдает осмысленного сообщения, а слетает на «Internal Error». Если inline-процедура используется за пределами про­грамм­но­го модуля, где она непосредственно описана, то трактуется как обычная вызываемая процедура.

Поддержка простых макросов. Определяется директивой компилятора <$MACRO ON/OFF>и ключом командной строки -Sm . Са­ми макросы определяются так <$DEFINE MACRO_NAME := EXPRESSION>, где MACRO_NAME — идентификатор, который в даль­ней­шем тексте программы будет заменяться на выражение EXPRESSION .

Поддержка специальных операторов присваивания в стиле Си. Определяется ключом командной строки -Sc , собственной ди­рек­ти­вы компилятора не имеет. Разрешает присваивание с одновременным сложением, вычитанием, умножением или делением, ис­поль­зуя операторы += , -= , *= и /= соответственно.

Перегрузка процедур и функций

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

procedure IncrementInteger(var Value: Integer);procedure IncrementReal(var Value: Real);

В языке Delphi существует возможность дать двум и более процедурам (функциям) одинаковые идентификаторы при условии, что все такие процедуры (функции) отличаются списком параметров (количеством параметров или типом параметров). Такая возможность называется перегрузкой. Для указания того, что процедура (функция) перегружена, служит стандартная директива overload. С ее помощью вышеприведенный пример можно переписать следующим образом:

procedure Increment(var Value: Integer); overload; // процедура 1procedure Increment(var Value: Real); overload; // процедура 2

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

Var X: Integer; Y: Real;begin X:=1; Y:=2.0; Increment(X); // Вызывается процедура 1 Increment(Y); // Вызывается процедура 2end.

Особенности перегрузки:

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

procedure Print(X: Shortint); overload; // процедура 1procedure Print(X: Longint); overload; // процедура 2

Если мы попробуем вызвать процедуру Print, указав в качестве фактического аргумента целочисленную константу, то увидим, что выбор компилятором варианта процедуры зависит от значения константы.

Print(5); // Вызывается процедура 1Print(150); // Вызывается процедура 2Print(-500); // Вызывается процедура 2Print(-1); // Вызывается процедура 1

Очевидно, что одно и то же число может интерпретироваться и как Longint, и как Shortint (например, числа 5 и –1). Логика компилятора в таких случаях такова: если значение фактического параметра попадает в диапазон значений нескольких типов, по которым происходит перегрузка, то компилятор выбирает процедуру (функцию), у которой тип параметра имеет меньший диапазон значений. Например, вызов Print(5) будет означать вызов того варианта процедуры, который имеет тип параметра Shortint. А вот вызов Print(150) будет означать вызов того варианта процедуры, который имеет тип параметра Longint, т.к. число 150 не вмещается в диапазон значений типа данных Shortint.

2. Поскольку в нынешней версии среды Delphi обощенный тип данных Integer совпадает с фундаментальным типом данных Longint, следующий вариант перегрузки является ошибочным:

procedure Print(X: Integer); overload;procedure Print(X: Longint); overload; // Ошибка!


3. Такая же ошибка возникает при использовании пользовательских типов данных, определенных через общий базовый тип.

Type TMyInteger = Integer; procedure Print(X: Integer); overload;procedure Print(X: TMyInteger); overload; // Ошибка!

Что делать в тех случаях, когда такая перегрузка просто необходима? Для этого пользовательский тип данных необходимо создавать с использованием ключевого слова type:

Type TMyInteger = type Integer; procedure Print(X: Integer); overload;procedure Print(X: TMyInteger); overload; // Правильно

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

procedure Increment(var Value: Real; Delta: Real = 1.0); overload;//процедура 1procedure Increment(var Value: Real); overload; //процедура 2

Вызов процедуры Increment с одним параметром вызовет неоднозначность (необходимо избегать одинаковых сигнатур подпрограмм до этих параметров):

Var X: Real;begin Increment(X, 10); // Вызывается процедура 1 Increment(X); // Ошибка! Неоднозначностьend.

5. Запрещается также перегружать функции, которые отличаются лишь типом возвращаемого значения.

function SquareRoot(X: Integer): Single; overload;function SquareRoot(X: Integer): Double; overload; // Ошибка!

6. Запрещается также перегружать подпрограммы, которые отличаются типом: в одном случае функция, в другом — процедура.

Дата добавления: 2020-02-09 ; просмотров: 523 ; ЗАКАЗАТЬ НАПИСАНИЕ РАБОТЫ

Interface

Begin

Begin

Begin

Type

Begin

Begin

T2.SetData (1); (2)

T2.SetData (1.1); (1)

Begin

Begin

Begin

Begin

End.

Begin

Статические методы.

По умолчанию методы класса являются статическими – их адрес определяется во время компиляции программы (раннее связывание), поэтому они вызываются быстрее всего.

В одноименных статических методах порожденных классов списки параметров могут отличаться.

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

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

Например:

type TFigure=class

procedure draw;

end;

TRectangle=class (TFigure)

end;

var Figure:TFigure;

Rectangle:TRectangle;

Figure.draw;


<вызывается метод draw класса TFigure>

Figure.free;

Figure.draw;

<вызывается метод draw класса TRectangle>

Figure.free;

Rectangle:=TRectangle.create; <описываем новую

Rectangle.draw;

<вызывается метод draw класса TRectangle>

Rectangle.free;

Здесь разные методы с именами draw вызываются в разных классах. Их адрес определяется при компиляции, поэтому статические методы вызываются быстрее всех.

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

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

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

DMT – Dynamic Method Table

VMT – Virtual Method Table

Далее компилятор поместит в них адреса точек входа соответственно динамических и виртуальных методов. При каждом обращении к замещаемому методу компилятор вставляет код, позволяющий извлечь адрес точки входа в подпрограмму из той или другой таблицы. А в классе – потомке замещающий метод объявляется с директивой Override (перекрыть). Получив это указание, компилятор создаст код, который при выполнении программы поместит в родительскую таблицу точку входа метода класса – потомка, что позволит родителю выполнить нужные действия с помощью нового метода.

Пример1. Пусть существует некоторый обобщенный класс для хранения данных и два его потомка для хранения строк и целых чисел:

Пример 1:

Type TField=class

function GetData: string; virtual; abstract;

end;

Function GetData: string; override;<метод

перекрыт>

end;

function GetData: string; override;

end;

function TStringField.GetData;<описывается метод

GetData класса TStringField>

Result:=FData;

end;

function TIntegerField.GetData; <описывается

метод GetData класса TIntegerField>

Result:=IntToStr(FData);

end;

Procedure Show (F:TField);

Form1.Label1.Caption:= F.GetData;

end;

В этой задаче классы TIntegerFieldиTStringField содержат разнотипные поля данных FDataи единственное что они умеют – это сообщать о значении своих данных текстовой строкой при помощи метода GetData

В каждом классе сообщается о значении разнотипных полей. Внешняя процедура Show получает объект в виде параметра и показывает строку данных. (Form1.Label1.Caption:= F.GetData;) В процедуре Showпараметр F описан как объект класса TField. Это значит, что в нее можно передавать объекты классов TStringField, TIntegerField как потомков класса TField.

Возникает вопрос: чей метод GetData при этом будет вызван? Ответ: тот, который соответствует классу фактически переданного объекта.

Т.о. правило соответствия типов языка Object Pascal гласит: объекту, как указателю на экземпляр объектного типа может быть присвоен адрес любого экземпляра любого из дочерних типов. Этот принцип называется полиморфизмом. Полиморфизм – наиболее важный принцип ООП.

В нашем примере программисту, пишущему процедуру Show важно лишь, что любой объект, переданный в нее, является потомком класса TField.


Если такую процедуру скомпилировать и поместить в динамическую библиотеку, то эту библиотеку можно будет раз и навсегда использовать без изменений, хотя будут появляться и новые, неизвестные в момент её создания классы-потомки TField.

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

В классе метод может быть объявлен абстрактным с помощью директивыabstract.

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

В рассмотренном выше ПРИМЕРЕ 1 можно рассмотреть, для чего нужно использование абстрактных методов. Классы, содержащие абстрактные методы, называются абстрактными. Такие классы инкапсулируют общие свойства своих неабстрактных потомков, но объекты абстрактных классов никогда не создаютсяи не используются. Их абстрактные методы не описываются. Для эксплуатации абстрактных классов в библиотеку классов Delphi включаются классы – потомки, в которых перекрываются абстрактные методы родителя. Таким способом абстрактные классы и абстрактные методы инкапсулируют доступ к методам потомков.

В ПРИМЕРЕ 1 класс TField не используется сам по себе, его абстрактный метод GetData не описывается (не имеет кода). Основное предназначение класса TField – быть родоначальником иерархии конкретных классов-потомков и дать возможность абстрагироваться от частности. Для описания абстрактных методов служит директива abstract, которая записывается после директивы virtual или dynamic.

Таблицы виртуальных и динамических методов – VMT и DMT.

Рассмотрим снова ПРИМЕР 1. Ясно, что у компилятора нет возможности определить класс объекта, фактически переданного в процедуру Show. Нужен механизм для определения класса объекта фактически переданного в процедуру Show, который позволяет определить это прямо во время выполнения программы. Это называется поздним связыванием. В качестве такого механизма служат таблицы VMT и DMT.

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

Отсюда достоинства и недостатки виртуальных методов. Они вызываются сравнительно быстро, однако, для хранения указателей на них в таблице VMT требуется большой объем памяти.

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

Значит бьльшая по размеру таблица VMT обеспечит более быстрый поиск. В то время как при обращении к динамическому методу программа сначала рассмотрит DMT у объекта, а затем, в случае неудачи, у всех классов – предков в порядке иерархии, пока не найдет нужную точку входа.

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

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

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

Type TFirst >

FMyField1:Integer;

FMyField2:LongInt;

Procedure StatMetod;

Procedure VirtMetod1; virtual;

Procedure VirtMetod2; virtual;

Procedure DynaMetod1; dynamic;

Procedure DynaMetod2; dynamic;

end;

Procedure StatMetod;

Procedure VirtMetod1; override;

Procedure DynaMetod1; override;

end;

Var Object1:TFirstClass;

Object2:TSecondClass;

Первый из методов StatMetod создается в новом классе заново <перекрывается статически>, остальные два перекрываются как виртуальный и динамический с помощью директивы override. Попытка применить директиву override к статическому методу вызовет ошибку компиляции.

Как устроен объект изнутри

Рассмотрим, как устроен объект изнутри и как происходит вызов методов в этом примере. Ясно, что каждый экземпляр класса (объект) содержит отдельную копию всех его полей. Ясно также, что внутри экземпляра есть указатели на таблицы VMT и DMT.

Нарисуем структуру объектов Object1 и Object2:

Указатель на объект Object1
Указатель на класс TFirstClass
Поле FMyField1
Поле FMyField2
Число динамических методов (2)
Индекс метода TFirstClass.DynaMetod1 (-1)
Индекс метода TFirstClass.DynaMetod2 (-2)
Адрес метода TFirstClass.DynaMetod1
Адрес метода TFirstClass.DynaMetod2
Информация класса TFirstClass
Адрес метода TFirstClass.VirtMethod1
Адрес метода TFirstClass.VirtMethod2
Указатель на объект Object2
Указатель на класс TSecondClass
Поле FMyField1
Поле FMyField2
Информация класса TSecondClass
Адрес метода TSecondClass.VirtMethod1
Адрес метода TSecondClass.VirtMethod2
Число динамических методов (1)
Индекс метода TSecondClass.DynaMetod1 (-1)
Адрес метода TSecondClass.DynaMetod1

Объект как экземпляр класса содержит указатель на его класс. Класс как структура состоит из двух частей. Начиная с адреса, на который ссылается указатель на класс, располагается таблица VMT. Она содержит адреса всех виртуальных методов класса, включая унаследованные от предков.

Отметим, что в объектах Object1 и Object2 длина таблиц VMT одинакова, в них по два элемента. Перед каждой VMT располагается специальная структура – информация класса. Одно из полей информации класса содержит адрес таблицы DMT класса. Таблица DMT имеет следующий формат: в начале – количество элементов таблицы, затем – индексы методов, нумерация которых начинается с (-1) и идет по убывающей. После индексов идут адреса динамических методов. Обратим внимание, что DMT объекта Object1 состоит из двух элементов, а объекта Object2 – из одного метода, соответствующего перекрытому методу DynaMetod1 в данном классе.

Внимание! В случае вызова Object2.DynaMetod2 индекс и адрес его не будут найдены в DMT Object2, тогда произойдет обращение к таблице DMT объекта Object1, в которой содержится адрес нужного метода DynaMetod2. В случае вызова Object2.VirtMetod2 адрес его сразу будет найден в таблице VMT объекта Object2.

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

Итоги:

Полиморфизм – это возможность описания одинакового по имени действия, применяемого ко всем объектам иерархии наследования, то есть возможность иметь несколько методов с одним и тем же именем для различных объектов одной иерархии. Это средство дает возможность развития объектов в потомках. Оно реализуется тем, что объект-потомок может добавлять и переопределять методы, то есть заменять методы предка на новые с теми же именами. Какой из методов будет выполняться при обращении к методу с заданным именем, определяется типом объекта (предок или потомок) и видом используемого метода (статический, виртуальный или динамический).


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

В ООП действуют следующие правила описания и наследования методов:

1. Служебные слова virtual и dynamic вводят (описывают) новый виртуальный или динамический метод;

2. Для перекрытия наследуемого статического метода никаких дополнительных служебных слов не используется. Но если в порожденном классе надо перекрыть наследуемый виртуальный или динамический метод используется служебное слово override;

3. В одноименных статических методах порожденных классов списки параметров могут отличаться. Списки параметров виртуальных и динамических методов порождающих и порожденных классов должны быть идентичными.

Перезагрузка методов.

Четвертая группа методов – перезагружаемые (Overload). Заметим, что и виртуальные и динамические методы могут быть перезагружаемыми. Перезагрузка нужна, чтобы произвести одинаковые или похожие действия с разнотипными данными. Статический метод перекрытия приводит к тому, что потомок «не видит» перекрытый родительский метод и может обращаться к нему только с помощью слова inherited, поэтому в Delphi используется перезагрузка, с помощью которой становятся видны одноименные методы как родителя, так и потомка.

Type TFirst=class

i:Extended;

procedure SetData(x:Extended);

end;

TSecond=class(TFirst)

j:Integer;

procedure SetData(AValue:Integer);

end;

var T2:TSecond;

В этом примере первый вызов метода SetData из объекта T2 с переменной =1.1 (не целая) вызовет ошибку! А второй вызов не приводит к ошибке, т.к. внутри объекта T2 статический метод с параметром типа Extended перекрыт одноименным методом с параметром типа Integer. Компилятор внутри T2 не признает параметр типа Extended.

Для доступа к методу SetData класса TFirst необходимо использовать служебное слово inherited. Например:

procedure TSecond.SetData;

x :=1.1;

inherited SetData (x);

j:=x;

end;

Но нам нужно произвести схожие действия (1), (2) с разнотипными данными в строках одной программы.

Может быть, метод сделать виртуальным?

Нет, нельзя! Поскольку тип и количество параметров в одном и том же виртуальном методе должны в точности совпадать.

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

Overload – директива, позволяющая перезагрузить методы:

Type TFirst=class

i:Extended;

procedure SetData(x:Extended); Overload;

end;

TSecond=class(TFirst)

j:Integer;

procedure SetData(AValue:Integer); Overload;

end;

var T2:TSecond;

Теперь в программе можно использовать метод как родителя, так и потомка.

T2.SetData (1.1);

T2.SetData (1);

Можно перезагрузить и виртуальный, и динамический методы. В этом случае надо добавить директиву reintroduce перед директивой overload.

ЗАДАЧА С ИСПОЛЬЗОВАНИЕМ ПОЛИМОРФИЗМА


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

Пусть определены 3 класса, один из которых является базовым для двух других:

fname:string;

constructor Create(name:string);

function info:string; virtual;

end;

constructor Create(name:string;gr:integer);

function info:string; override;

end;

constructor Create(name:string; dep:string);

function info:string; override;

end;

В каждом из этих классов определен метод info.В базовом классе при помощи директивы virtualметодinfoобъявлен виртуальным. Это дает возможность классу–потомку произвести замену виртуального метода своим собственным. В каждом классе–потомке определен свой метод info,который замещает соответствующий метод родительского класса и отмечается директивой override.

Определим метод infoдля каждого класса индивидуально:

function Tperson.info:string;

result:=’’;

end;

function Tstud.info:string;

result:=fname+’ gruppa ‘+inttostr(fgr);

end;

function Tprof.info:string;

result:=fname+’ department ‘+fdep;

end;

Далее в программе список всех людей можно представить массивом объектов класса Tperson. Отметим, что объект – указатель.

Список людей имеет вид:

Объявить подобным образом список можно потому, что OP позволяет присвоить указателю на родительский класс значение указателя на класс– потомок. Поэтому элементами массива listмогут быть как объекты класса Tstud,так и объекты классаTprof.

Вывод списка можно осуществить применением метода infoк элементам массива, например:

St:= ’’;

for i:=1 to szl do

if list[i]<>nil then

St:=St+list[i].info+#13;

Во время работы программы каждый элемент массива может содержать как объект типа Tstud,так и объект типаTprof.

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

Напишем программу, которая использует объявления классов Tperson, Tstud, Tprof,формирует список студентов и преподавателей и выводит полученный список в окно сообщения. Будем использовать визуальное программирование.

Окно формы будет иметь вид:

GroupBox1—это компонент , объединяющий группу компонентов, связанных по смыслу. В данном случае он включает 2 зависимых переключателя – RadioButton1 и RadioButton2

Текст модуля кода программы:

unit Polimorfizm;

Не нашли то, что искали? Воспользуйтесь поиском:

Лучшие изречения: Как то на паре, один преподаватель сказал, когда лекция заканчивалась — это был конец пары: «Что-то тут концом пахнет». 8373 — | 8006 — или читать все.

188.64.174.135 © studopedia.ru Не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования. Есть нарушение авторского права? Напишите нам | Обратная связь.

Отключите adBlock!
и обновите страницу (F5)

очень нужно

Илон Маск рекомендует:  Выделение всего текста в текстовом поле
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL