![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Очередные баги компилятора.
oxffff © (2007-04-09 15:41:00)Сегодня рассмотрим const передачу интерфейсов
Простой пример
procedure abc(const a:Iunknown);
var b:Iunknown;
begin
b:=a;
b:=nil;
end;
procedure TForm1.Button1Click(Sender: TObject);
var a:TInterfacedObject;
begin
a:=TInterfacedObject.Create;
abc(a);
// a уже разрушен
end;
Здесь после вызова abc объект а уже разрушен.
Аналогичный рабочий пример используя временную переменную
procedure TForm1.Button1Click(Sender: TObject);
var a:TInterfacedObject;
begin
a:=TInterfacedObject.Create;
abc(a as IUnknown);
// a еще жив
end;
Здесь после вызова abc объект a еще неразрушен.
Выводы.
Компилятор нужно поправить, чтобы при передачи const интерфейса нужно создавать временную переменную, а не использовать в качестве параметра внутрение поля объекта.
Выявленная ошибка работает на D7, BDS 2006 SP2
oxffff (2007-04-09 16:45:00) [21]
> SlymRO © (09.04.07 16:28) [8]
> Джо © (09.04.07 16:20) [4]
> abc(a as IUnknown);
> От тута временная переменная (накрутка счетчика)
> abc(a); - так накрутки не происходит, но происходит декрутка
> :) внутри abc
> сей баг аналогичен этому:
> var
> a:TInterfacedObject;
> b:IUnknown;
> begin
> a:=TInterfacedObject.Create;
> try
> b:=a as IUnknown;
> b:=nil;
> finally
> a.Free;
> end;
> end;
Сей пример неудачен. Это не аналогия.
oxffff (2007-04-09 16:43:00) [22]
> Джо © (09.04.07 16:29) [9]
> А, не понял сначала, об чем речь, сорри.
То есть при приведении объекта к интерфейсу, нужно создавать временную
переменную, чтобы гарантировано продлить жизнь объекту.
Компилятор использует внутрениие поля объекта. Поэтому установить состояние объекта после возвращение из функции невозможно.
Поскольку он уже может быть разрушен, а может и нет.
oxffff (2007-04-09 16:37:00) [23]Вот пример
TSomeClass=class(TinterfacedObject)
protected
SomeText:string;
public
procedure Myproc;virtual;
constructor create;
end;
procedure abc(const a:Iunknown);
var b:Iunknown;
begin
b:=a;
b:=nil;
end;
constructor TSomeClass.create;
begin
inherited;
SomeText:='THIS STRING REFERENCE WILL BE EMPTY AFTER DESTRUCTION';
end;
procedure TSomeClass.Myproc;
begin
showmessage(SomeText);
end;
procedure TForm1.Button1Click(Sender: TObject);
var a:TSomeClass;
begin
a:=TSomeClass.Create;
abc(a);
a.Myproc;
//А После этого уже нет
end;
jack128 (2007-04-09 16:32:00) [24]ИМХО, это НЕ баг.
Джо (2007-04-09 16:29:00) [25]А, не понял сначала, об чем речь, сорри.
SlymRO (2007-04-09 16:28:00) [26]Джо © (09.04.07 16:20) [4]
abc(a as IUnknown);
От тута временная переменная (накрутка счетчика)
abc(a); - так накрутки не происходит, но происходит декрутка :) внутри abc
сей баг аналогичен этому:
var
a:TInterfacedObject;
b:IUnknown;
begin
a:=TInterfacedObject.Create;
try
b:=a as IUnknown;
b:=nil;
finally
a.Free;
end;
end;
oxffff (2007-04-09 16:28:00) [27]Еще раз
procedure TForm1.Button1Click(Sender: TObject);
var a:TInterfacedObject;
begin
a:=TInterfacedObject.Create;
abc(a);
a.someProc //приведет к сбою в случае если abc работает со счетчиком ссылок. Но это установить нельзя по причине того, что abc может быть произвольной
end;
procedure TForm1.Button1Click(Sender: TObject);
var a:TInterfacedObject;
begin
a:=TInterfacedObject.Create;
abc(a as Inknown);
a.someProc // обработает нормально
end;
Понятно?
Конечно можно сделать так, чтобы гарантировано после abc А был жив
procedure TForm1.Button1Click(Sender: TObject);
var a:TInterfacedObject;
b:iunknown;
begin
a:=TInterfacedObject.Create;
b:=a;
abc(a);
end;
Но хочеться чтобы это делал компилятор.
oxffff (2007-04-09 16:23:00) [28]
> Джо © (09.04.07 16:20) [4]
> > Здесь после вызова abc объект a еще неразрушен.
>
>
> type
> TMyInterfacedObject = class (TInterfacedObject, IUnknown)
> public
> destructor Destroy; override;
> end;
>
> implementation
>
> { TMyInterfacedObject }
>
> destructor TMyInterfacedObject.Destroy;
> begin
> ShowMessage ('Destroyed');
> inherited;
> end;
>
> procedure abc(const a:Iunknown);
> var
> b:IUnknown;
> begin
> b := a;
> b := nil;
> end;
>
> procedure TForm1.Button1Click(Sender: TObject);
> var
> a: TMyInterfacedObject;
> begin
> a:=TMyInterfacedObject.Create;
> abc(a as IUnknown);
> // Разрушается
> end;
>
> end.
>
>
> D7, все корректно разрушается.
Конечно все разрушается, но уже финализатором.
А я про момент тогда мы еще не дошли до финализатора.
oxffff (2007-04-09 16:21:00) [29]
> ** не смог представить случай, когда такой изврат может
> понадобиться **
Ну это собственно пример.
Процедура abc может копировать, запрашивать интерфейсы вынуждая вызовы _addref, _Release, а может этого не делать.
Поэтому в первом случае
abc(А) нельзя гарантровано сказать жив объект А, или мерт.
Во втором случае
abc(А as Iunknown)
А жив всегда после возврата из abc.
Джо (2007-04-09 16:20:00) [30]> Здесь после вызова abc объект a еще неразрушен.
type
TMyInterfacedObject = class (TInterfacedObject, IUnknown)
public
destructor Destroy; override;
end;
implementation
{ TMyInterfacedObject }
destructor TMyInterfacedObject.Destroy;
begin
ShowMessage ('Destroyed');
inherited;
end;
procedure abc(const a:Iunknown);
var
b:IUnknown;
begin
b := a;
b := nil;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
a: TMyInterfacedObject;
begin
a:=TMyInterfacedObject.Create;
abc(a as IUnknown);
// Разрушается
end;
end.
D7, все корректно разрушается.
clickmaker (2007-04-09 16:15:00) [31]
> procedure abc(const a:Iunknown);
> var b:Iunknown;
> begin
> b:=a;
> b:=nil;
> end;
** не смог представить случай, когда такой изврат может понадобиться **
oxffff (2007-04-09 16:06:00) [32]
> Объекты вроде всегда по ссылке передавались. Они не являються
> простым типом данных.
Здесь при передачи переменная а приводится Iunknown.
tesseract (2007-04-09 16:03:00) [33]
> Компилятор нужно поправить, чтобы при передачи const интерфейса
> нужно создавать временную переменную,
Объекты вроде всегда по ссылке передавались. Они не являються простым типом данных.