|
»» FreePascal »» Lazarus »» MSEide + MSEgui »» Разное »» Книги |
28.06.2007 Николай Лабинский Обобщения aka GenericsРазработчикам, использующим объектно-ориентированное программирование, хорошо известны его преимущества. Одно из ключевых преимуществ — возможность повторно использовать код, т.е. создавать производный класс, наследующий все возможности базового класса. В производном классе можно просто переопределить виртуальные методы или добавить новые, чтобы изменить унаследованные характеристики для решения новых задач. Обобщения (Generics) — еще один новый (начиная с версии 2.2.х) механизм повторного использования кода, а именно повторным использованием алгоритма. По сути, разработчик определяет алгоритм, например сортировку, поиск, замену,
преобразование и т.д., но не указывает конкретный тип данных, с которым работает
алгоритм. Именно поэтому алгоритм можно обобщенно применять к объектам разных
типов. Используя готовый алгоритм, другой разработчик просто указывает
конкретный тип, например для сортировки — В FPC обобщения реализованы как своего рода макросы для компилятора, которые он выполняет при специализации (specialize), т.е. при их непосредственном использовании при указании конкретного типа. Именно поэтому описание и использование обобщений происходит за два этапа:
Рассмотрим, как же описываются обобщения в FPC на простом примере списка: type
generic GList<_T> = class
type public // Область типов (публичная)
// Тип функции для метода ForEach
TForEachProc = procedure(item: _T);
var private // Область полей (приватная)
Arr : array of _T; // В основе списка лежит динамический массив
Len : integer; // Длина массива
public // Область публичных методов
function Add(item : _T): integer;
procedure DeleteAt(p : integer);
procedure ForEach(p : TForEachProc);
procedure Clear;
constructor Create;
destructor Destroy; override;
end;
Ну и реализация методов: function GList.Add(item : _T): integer;
begin
SetLength(Arr,Len+1);
Arr[Len] := item;
Result := Len;
inc(Len);
end { Add };
procedure GList.DeleteAt(p : integer);
var
i : integer;
begin
if (p >= 0) and (p < Len) then
begin
for i := p to Len-2 do
Arr[i] := Arr[i+1];
dec(Len);
SetLength(Arr,Len);
end;
end { DeleteAt };
procedure GList.ForEach(p : TForEachProc);
var
i : integer;
begin
for i:= Low(Arr) to High(Arr) do
p(Arr[i]);
end { ForEach };
procedure GList.Clear;
begin
Arr := nil;
Len := 0;
end { Clear };
constructor GList.Create;
begin
inherited;
Len := 0;
end { Create };
destructor GList.Destroy;
begin
Clear;
inherited;
end { Destroy };
Как видно из примера, описание обобщений очень похоже не описание обычного класса за исключением локальных блоков описаний типов и переменных как в модулях или подпрограммах. Рассмотрим некоторые особенности описания и реализации:
Рассмотрим теперь специализацию обобщений. Однажды описанное обобщение может быть использовано для генерации других классов: это похоже на повторение описания класса только уже с шаблонами, указывающими на конкретные типы данных. Специализация возможна только в блоках type TGL_int = specialize GList<integer>; TGL_str = specialize GList<string>; Описание же переменных с использованием специализации запрещено: var TGL_smpl : specialize GList<integer>; // Ошибка Кроме того, тип специализации (тот что в угловых скобках) должен быть известен. Рассмотрим пример: type Generic TMyFirstType Ошибка возникает потому, что тип type TA = specialize TMyFirstType<Atype>; TB = specialize TMySecondType<TA>; потому что Но стоит заметить, что две одинаковые специализации одного и того же шаблона нельзя присваивать друг другу что само собой вытекает из правил эквивалентности типов… Эти 2 типа просто не эквивалентны, только поэтому (Generic-и тут ни при чем) нельзя присваивать друг другу переменные разных типов (спасибо volvo877). Например тут: type TA = specialize GList<integer>; TB = specialize GList<integer>; var A : TA; B : TB; begin A := B; // Ошибка! присвоение Ну и в конце — пример использования: {$mode objfpc}
uses GnrcLst;
type
TGL_int = specialize GList<integer>;
TGL_str = specialize GList<string>;
var
l1 : TGL_int;
l2 : TGL_str;
procedure ForEach_int(item : integer);
begin
WriteLn(item)
end { ForEach_int };
procedure ForEach_str(item : string);
begin
WriteLn(item)
end { ForEach_int };
begin
l1 := TGL_int.Create;
l1.Add(3);
l1.Add(7);
l1.Add(15);
Writeln('Список integer''ов:');
l1.ForEach(@ForEach_int);
l1.DeleteAt(1);
Writeln('Список integer''ов после удаления 1го элемента:');
l1.ForEach(@ForEach_int);
l1.Free;
WriteLn;
l2 := TGL_str.Create;
l2.Add('1th');
l2.Add('2th');
l2.Add('3th');
Writeln('Список string''ов:');
l2.ForEach(@ForEach_str);
l2.DeleteAt(1);
Writeln('Список string''ов после удаления 1го элемента:');
l2.ForEach(@ForEach_str);
l2.Free;
end.
И его результаты работы: Running "d:ppworkt_gnrclst.exe " Список integer'ов: 3 7 15 Список integer'ов после удаления 1го элемента: 3 15 Список string'ов: 1th 2th 3th Список string'ов после удаления 1го элемента: 1th 3th Послесловие, или что все это дает?..Перечислю пару плюсов/минусов обобщений: [+] Безопасность типов. Когда обобщенный алгоритм
специализируется компилятор понимает это и не допускает работу с другими типами.
Так, вы не сможете в [+] Более простой и понятный код. Поскольку компилятор обеспечивает безопасность типов, в исходном коде нужно меньше приведений типов. И как следствие, такой код проще писать и поддерживать. [-] «Распухание» кода. Компилятор будет генерировать машинный код для каждого сочетания «обобщение + специализация», что в итоге может привести к увеличению размера приложения. [-] Новизна. В FPC обобщения только-только появляются и многие возможности пока еще не реализованы. К ним относится и отсутствие поддержки Generic-ов в процедурах/функциях что привносит некоторые неудобства… P.S. Все исходники можно найти в аттаче. 2007 © Nikolay Labinskiy aka e-moe При написании использовались:
|
|||
| Если у Вас есть материалы для добавления - обращайтесь к авторам проекта. |
| FPC | 2.6.0 | release |
| Lazarus | 0.9.30.4 | release |
| 0.9.31 | ||
| MSE | 2.8.2 | release |
| Мягкая игрушка |
| Архив журнала с 2004 года. Каталог, описания, цены. |
| oktoys.ru |