popoveo писал(а): опять не понятно, где увеличивать и уменьшать количество ссылок
При любой передаче объекта «по значению», а тот, кому передали, уменьшает, когда закончит работать. В частности: функция ожидает аргументы инкрементнутыми и освобождает их; если функция возвращает объект, она инкрементирует его счётчик, а когда вызвавший закончит с ним работать — декрементирует; конструкторы всегда возвращают объект со счётчиком = 1. Ну, так в моём варианте (и так компилятор работает со всеми автотипами, кроме интерфейсов).
Интерфейсы чуть по-другому сделаны (предполагается, что конструктор возвращает объект с нулём ссылок), поэтому у них есть неприятная багофича:
- Код: Выделить всё
{$mode objfpc} {$h+} {$apptype console} {$modeswitch duplicatelocals}
uses
heaptrc;
type
IString = interface
function GetValue: string;
property Value: string read GetValue;
end;
StringWrapper = class(TInterfacedObject, IString)
value: string;
constructor Create(const value: string);
function GetValue: string;
end;
constructor StringWrapper.Create(const value: string);
begin
inherited Create;
self.value := value;
end;
function StringWrapper.GetValue: string;
begin
result := value;
end;
operator +(a, b: IString): IString;
begin
result := StringWrapper.Create(a.Value + b.Value);
end;
procedure Test;
begin
writeln((StringWrapper.Create('1') + StringWrapper.Create('2') + StringWrapper.Create('3')).Value);
end;
begin
Test;
DumpHeap;
readln;
end.
Если в operator+ передать const-ссылку на объект, созданный на месте (здесь операнды такими и являются), его счётчик никогда не увеличится с нуля, соответственно, не уменьшится и объект утечёт. Это не считая того, что подсчёт ссылок работает только для переменных интерфейсного типа, а не классового, поэтому, единожды отдав класс в интерфейсную переменную, в дальнейшем с ним можно работать только через интерфейсы.