Reputation: 452
I got a doubt about freeing an Interfaced object.
My interface looks like this:
type IBase = interface(IInterface)
function test: Boolean;
end;
This other class have some attributes that i wanted in all the interfaced classes:
type TBase = class(TInterfacedObject)
protected
FQuery: TADOQuery;
FADOConnection: TADOConnection;
public
constructor Create; virtual; abstract;
destructor Destroy;
end;
I got some clases that inherits from the previous class and also implements the interface.
type TExample= class(TBase, IBase)
public
function test: Boolean;
destructor Destroy;
end;
So, using this schema I'm able to use the classes like this:
procedure someProcedure(aux : IBase);
begin
aux.test; //Aux is an instance of TExample and I'm using the interfaced method
end;
My question is,How I destroy this aux object that is a IBase interfacedObject? I tried this things, checking first that is not a nil object:
(aux as TObject).Destroy; //Invalid Pointer operation
(aux as TInterfacedObject).Destroy; //Invalid Pointer operation
(aux as TExample).Destroy; Also invalid Pointer operation!!??
Instead of freeing the object, I read that because inherits from TInterfacedObject and implements the interface I should use this:
aux := nil;
And the reference counter will do the magic, but using ReportMemoryLeaksOnShutdown := True; in my project there are some leaks and the code never reach the breakpoint of the destructor.
Am I missing something??
EDIT:
I changed my constructors, now are like:
type TBase = class(TInterfacedObject)
protected
FQuery: TADOQuery;
FADOConnection: TADOConnection;
public
constructor Create;
destructor Destroy; override;
end;
type TExample= class(TBase, IBase)
public
function test: Boolean;
end;
Now i guess that make more sense since the TBase is allocating and freeing the objects and the TExample class inherits the destructor.
Upvotes: 4
Views: 3164
Reputation: 4878
Your example is somehow strange.
Since you have declared the TBase
constructor as virtual abstract
, a constructor has to be declared in the TExample
class.
As others have said, you have to add the override
directive to your destructors.
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
FastMM4,
System.SysUtils, ADODB, ActiveX;
type
IBase = interface(IInterface)
function test: Boolean;
end;
TBase = class(TInterfacedObject)
protected
FQuery: TADOQuery;
FADOConnection: TADOConnection;
public
constructor Create; virtual; abstract;
destructor Destroy; override;
end;
TExample= class(TBase, IBase)
public
function test: Boolean;
constructor Create; reintroduce; virtual;
destructor Destroy; override;
end;
{ TBase }
destructor TBase.Destroy;
begin
FQuery.Free;
FADOConnection.Free;
inherited;
end;
{ TExample }
constructor TExample.Create;
begin
//inherited;
FADOConnection := TADOConnection.Create(nil);
FQuery := TADOQuery.Create(nil);
end;
destructor TExample.Destroy;
begin
inherited;
end;
function TExample.test: Boolean;
begin
Result := False;
end;
var
example: IBase;
begin
CoInitialize(nil);
example := TExample.Create;
try
WriteLn(example.test);
finally
example := nil;
end;
CoUninitialize;
end.
Another thing that looks weird to me is freeing the FADO
objects in the base destructor since they're created in a derived class.
As a side note, I'd prefer a design like this:
type
TBase = class(TInterfacedObject)
protected
FQuery: TADOQuery;
FADOConnection: TADOConnection;
public
constructor Create;
destructor Destroy; override;
end;
TExample= class(TBase, IBase)
public
function test: Boolean;
constructor Create;
destructor Destroy; override;
end;
{ TBase }
constructor TBase.Create;
begin
inherited;
FADOConnection := TADOConnection.Create(nil);
FQuery := TADOQuery.Create(nil);
end;
destructor TBase.Destroy;
begin
FQuery.Free;
FADOConnection.Free;
inherited;
end;
{ TExample }
constructor TExample.Create;
begin
inherited;
. . .
end;
Upvotes: 1
Reputation: 6402
There are a few problems here:
First of all you need to add override
to the destructor as pointed out in the comment. But the declaration of TBase
is realy what is killing the whole thing:
You have an virtual empty constructor
which hides the one from TObject. And besides from that when you have a TInterfacedObject
you should work with the interface not the instance.
so
type TBase = class(TInterfacedObject)
protected
FQuery: TADOQuery;
FADOConnection: TADOConnection;
public
constructor Create; virtual; abstract;
destructor Destroy;
end;
Should really be
TBase = class(TInterfacedObject)
protected
FQuery: TADOQuery;
FADOConnection: TADOConnection;
public
constructor Create; reintroduce; virtual;
destructor Destroy; override;
end;
NOTE reintroduce
.
With this in hand, and the override added to the destructors you can free you object as you are use to :
procedure TForm1.FormCreate(Sender: TObject);
var
aux: TExample;
begin
aux := TExample.Create;
aux.Free;
end;
No need to cast it. Here you created an intance you self therefor you have to free it youself
But as I said before when you have a TInterfacedObject you should work with the interface and the object will free it self when it is not referenced any more.
So the example from before should realy have been like this:
procedure TForm1.FormCreate(Sender: TObject);
var
aux: iBase;
begin
aux := TExample.Create;
//do stuff with aux
end;
My complete test code looks like this :
type
IBase = interface(IInterface)
function test: Boolean;
end;
TBase = class(TInterfacedObject)
protected
FQuery: TADOQuery;
FADOConnection: TADOConnection;
public
constructor Create; reintroduce; virtual;
destructor Destroy; override;
end;
TExample = class(TBase, IBase)
public
function test: Boolean;
destructor Destroy; override;
end;
{ TBase }
constructor TBase.Create;
begin
inherited;
end;
destructor TBase.Destroy;
begin
inherited;
end;
{ TExample }
destructor TExample.Destroy;
begin
test;
inherited;
end;
function TExample.test: Boolean;
begin
ShowMessage('');
end;
Then just call it:
procedure TForm1.FormCreate(Sender: TObject);
var
aux: iBase;
begin
aux := TExample.Create;
end;
Upvotes: -2
Reputation: 45343
Don't free interfaced objects manually!
When a class derives from TInterfacedObject
then it is automatically reference counted and will be freed automatically as soon as there is no interface reference to it left.
What does it mean?
The following procedure references an instance of TExample
over its interface IBase
. It clears up memory automatically when the Obj
variable is being removed from stack.
procedure Foo;
var
Obj: IBase;
begin
Obj := TExample.Create; // reference count will be set to 1
Obj.test;
end; // reference count will be set to 0 and Obj will be freed
The next procedure references an instance of TExample
over its class name. The reference counting is inactive here. The compiler includes no calls to _AddRef
and _Release
. So this procedure leaks memory:
procedure Foo;
var
Obj: TExample;
begin
Obj := TExample.Create;
Obj.test;
end; // Obj will not be freed automatically
So you would need to clean the heap on your own like this:
procedure Foo;
var
Obj: TExample;
begin
Obj := TExample.Create;
try
Obj.test;
finally
Obj.Free;
end;
end;
It works but it can be dangerous when Obj
is passed around. As soon as a reference to the object is stored in an interface reference. Let's see this:
procedure Bar(Obj: IBase);
begin
//...
end;
procedure Foo;
var
Obj: TExample;
begin
Obj := TExample.Create;
try
Bar(Obj);
Obj.test; // Access violation!
finally
Obj.Free;
end;
end;
What happens here?
Obj
is created and stored as a class reference. The reference count is 0. When Bar(Obj)
is called the object will be stored in an interface reference. The compiler includes calls to _AddRef
and _Release
when Bar
is called. The reference counter will be increased and decreased, so it becomes 0 again and the object destroys itself.
How to deal with it?
TInterfacedObject
by its concrete class. Upvotes: 5