Sign in Join
Wiki copytemporary

Templates/ru

English · Français · Русский

Вступление

Note: Шаблоны, представленные на этой странице, больше не нужны, потому что поддержка Generics в FPC полная и достаточно удобная для использования.

Templates (шаблоны) - это простой механизм для решения проблемы написания дублирующего кода для реализации общего класса для конкретных типов в строго типизированном языке. Используется в основном для базовых (необъектных) типов; для объектов наследования от одного базового класса TObject обычно используется для построения общих классов. В продвинутых языках шаблоны заменяются нативно-языковой реализацией, так называемыми "дженериками". Собственная реализация дженериков в FPC 2.4.0 еще не является ни полной, ни практически используемой. Пока это не так, могут быть использованы шаблоны.

Плюсы:

Минусы:

Использование

Классическим примером базового класса-дженерика является список элементов. Free Pascal в настоящее время предлагает три способа использования списков:

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

GenericList.inc:

pascal
{$IFDEF INTERFACE}     
  // TGList<T> = class
  TGList = class
    Items: array of T;
    procedure Add(Item: T);
  end;
{$UNDEF INTERFACE}     
{$ENDIF}     

{$IFDEF IMPLEMENTATION}     
procedure TGList.Add(Item: T);
begin
  SetLength(Items, Length(Items) + 1);  
  Items[Length(Items) - 1] := Item;
end;
{$UNDEF IMPLEMENTATION}     
{$ENDIF}

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

ListInteger.pas:

pascal
unit ListInteger;
{$mode ObjFPC} 
interface

uses
  Classes;

type
  T = Integer; // T указывается для какого-то точного типа
{$DEFINE INTERFACE}
{$INCLUDE 'GenericList.inc'}

type
  // TListInteger<Integer> = class
  TListInteger = class(TGList)
    // Дополнительные поля и методы могут быть добавлены здесь
  end;

implementation

{$DEFINE IMPLEMENTATION}
{$INCLUDE 'GenericList.inc'}

end.

Наконец, у нас есть новый специализированный модуль, названный ListInteger, и мы можем использовать наш новый специализированный тип в некотором коде.

pascal
program GenericTest;
{$mode ObjFPC}
uses
  ListInteger;
var
  List: TListInteger;
begin
  try
    List := TListInteger.Create;
    List.Add(1);
    List.Add(2);
    List.Add(3);
  finally
    List.Free;
  end;
end.

Наследование

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

Предположим, что мы хотим создать расширенный список дженериков TAdvancedList, у которого есть поле Capacity.

Определение базового класса-дженерика:

GenericList.inc:

pascal
{$IFDEF INTERFACE}     
  // TGList<TListIndex, TListItem> = class
  TGList = class
    Items: array[TListIndex] of TListItem;
    procedure Add(Item: TListItem);
  end;
{$UNDEF INTERFACE}
{$ENDIF}

{$IFDEF IMPLEMENTATION}
procedure TGList.Add(Item: TListItem);
begin
  SetLength(Items, Length(Items) + 1);  
  Items[Length(Items) - 1] := Item;
end;
{$UNDEF IMPLEMENTATION}
{$ENDIF}

Определение для расширенного класса-дженерика:

GenericAdvancedList.inc:

pascal
{$IFDEF INTERFACE}     
  TListIndex = TAdvancedListIndex;
  TListItem = TAdvancedListItem;
{$DEFINE INTERFACE}     
{$INCLUDE 'GenericList.inc'}
  // TGAdvancedList<TAdvancedListIndex, TAdvancedListItem> = class(TGList)
  TGAdvancedList = class
    Capacity: TAdvancedListIndex;
  end;
{$UNDEF INTERFACE}
{$ENDIF}

{$IFDEF IMPLEMENTATION}
{$INCLUDE 'GenericList.inc'}
{$UNDEF IMPLEMENTATION}
{$ENDIF}

Пример без некоторой специализации:

AdvancedListInteger.pas:

pascal
unit AdvancedListInteger;

interface

uses
  Classes;

type
  TAdvancedListIndex = Integer; // указан некоторый точный тип
  TAdvancedListItem = Integer; // указан некоторый точный тип
{$DEFINE INTERFACE}
{$INCLUDE 'GenericAdvancedList.inc'}

type
  // TAdvancedListInteger<Integer, Integer> = class
  TAdvancedListInteger = class(TGAdvancedList)
    // Дополнительные поля и методы могут быть добавлены здесь
  end;

implementation

{$DEFINE IMPLEMENTATION}
{$INCLUDE 'GenericAdvancedList.inc'}

end.

Теперь у нас есть специализированный класс из класса-дженерика, который наследуется от другого класса-дженерика.

Ограничения

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

Теоретически ограничиваемые группы:

Классы-дженерики

Есть много классов, которые могут извлечь выгоду из существования дженериков:

There are some interesting specialized types which can replace non-generic types: Есть несколько интересных специализированных типов, которые могут заменить типы-не-дженерики:

Specialized generic class Similar data type
TList<Char> string
TList<WideChar> WideString
TList<Byte> array of Byte
TList<string> Classes.TStrings
TList<Pointer> Classes.TList
TList<Boolean> Classes.TBits
TList<TObject> Contnrs.TObjectList
TList<TComponent> Contnrs.TComponentList
TList<TClass> Contnrs.TClassList
TStack<Pointer> Contnrs.TStack
TStack<TObject> Contnrs.TObjectStack
TQueue<Pointer> Contnrs.TQueue
TQueue<TObject> Contnrs.TObjectQueue
TStream<Byte> Classes.TMemoryStream
TDictionary<TPair<string, string>> Classes.TStringList
TList<TMethod> LCLProc.TMethodList
TTree<TTreeNode> ComCtrls.TTreeView
TPoint<Integer> Classes.TPoint
TPoint<SmallInt> Classes.TSmallPoint
TRectangle<Integer> Classes.TRect

Существующие библиотеки дженериков

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

Некоторые готовые к использованию классы:

См. также

Внешние ссылки


From the Free Pascal wiki, page "Templates/ru", as it stood on 2020-06-04 after a change by Zoltanleo, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 1, last changed by WikiArchive .

Edited on wiki.freepascal.org by 3 people, 2018–2020

From the official wiki's backup of 26 May 2024: who edited this page there, and when. Edits made there after that date were not recorded, and the earlier versions are not shown here; the main page says why.

WhenWhoWhat changed
ZoltanleoCreated page
ZoltanleoВступление
ZoltanleoUsage
ZoltanleoИспользование
ZoltanleoInheritance
ZoltanleoНаследование
ZoltanleoConstrains
ZoltanleoGeneric classes
ZoltanleoExisted generic libraries
ZoltanleoСуществующие библиотеки дженериков
ZoltanleoSee also
ZoltanleoExternal links
ZoltanleoКлассы-дженерики
ZoltanleoКлассы-дженерики
FTurtleDeleted English category
TrevFixed syntax highlighting (minor)
Zoltanleo

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.