2015-11-06 42 views
3

我對釋放接口對象有疑問。釋放接口對象

我的界面看起來是這樣的:

type IBase = interface(IInterface) 
    function test: Boolean; 
end; 

這其他類有一些屬性,我想在所有的接口類:

type TBase = class(TInterfacedObject) 
    protected 
     FQuery: TADOQuery; 
     FADOConnection: TADOConnection; 
    public 
     constructor Create; virtual; abstract; 
     destructor Destroy; 
    end; 

我得到了一些clases從以前的類繼承和也實現了界面。

type TExample= class(TBase, IBase) 
    public 
     function test: Boolean; 
     destructor Destroy; 
end; 

因此,使用這種模式我能使用的類是這樣的:

procedure someProcedure(aux : IBase); 
begin 
    aux.test; //Aux is an instance of TExample and I'm using the interfaced method 
end; 

我的問題是,我如何摧毀這個輔助對象,它是一個廣積interfacedObject? 我想這件事,首先檢查這不是一個零對象:

(aux as TObject).Destroy; //Invalid Pointer operation 
(aux as TInterfacedObject).Destroy; //Invalid Pointer operation 
(aux as TExample).Destroy; Also invalid Pointer operation!!?? 

而不是釋放的對象,我讀了,因爲從TInterfacedObject繼承並實現接口我應該使用這樣的:

aux := nil; 

而引用計數器會做到這一點,但使用ReportMemoryLeaksOnShutdown:= True;在我的項目中有一些泄漏,代碼永遠不會到達析構函數的斷點。

我錯過了什麼?

編輯

我改變了我的構造函數,現在是這樣的:

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; 

現在,我想這更有意義,因爲TBASE的分配和釋放對象和TExample類繼承的析構函數。

+3

您需要爲'destructor'聲明添加'override;'。除此之外,你不應該做任何明確的事情來釋放對象。 –

+0

這就是......把它當成一個問題,我會給你最好的答案......謝謝! – Izuel

+0

那還不夠。它仍然會失敗 –

回答

-2

這裏有幾個問題:

首先你需要爲添加override析構函數指出了評論。但TBase的聲明是真的什麼是殺了整件事:

你有一個虛擬的空的constructor隱藏TObject的一個。除此之外,當你有一個TInterfacedObject你應該使用接口而不是實例。

所以

type TBase = class(TInterfacedObject) 
    protected 
     FQuery: TADOQuery; 
     FADOConnection: TADOConnection; 
    public 
     constructor Create; virtual; abstract; 
     destructor Destroy; 
    end; 

真的應該

TBase = class(TInterfacedObject) 
    protected 
    FQuery: TADOQuery; 
    FADOConnection: TADOConnection; 
    public 
    constructor Create; reintroduce; virtual; 
    destructor Destroy; override; 
    end; 

注意reintroduce

有了這個在手,並添加到析構函數倍率可以釋放你的對象,你是使用到:

procedure TForm1.FormCreate(Sender: TObject); 
var 
    aux: TExample; 
begin 
    aux := TExample.Create; 
    aux.Free;  
end; 

無需投放。在這裏你創建了一個你自己的實例,你必須自己釋放它

但正如我之前說過的,當你有一個TInterfacedObject時,你應該使用接口,當它不再被引用時,它將自己釋放它。

所以從之前的例子應該真的一直是這樣的:

procedure TForm1.FormCreate(Sender: TObject); 
var 
    aux: iBase; 
begin 
    aux := TExample.Create; 
    //do stuff with aux 
end; 

我完整的測試代碼如下所示:

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; 

然後叫它:

procedure TForm1.FormCreate(Sender: TObject); 
var 
    aux: iBase; 
begin 
    aux := TExample.Create; 
end; 
+0

正確,但我選擇告訴他爲什麼不能釋放對象 –

+2

你永遠不應該將引用計數的對象實例存儲在對象引用中,否則你會中斷引用計數。使用'aux:IBase'而不是'aux:TExample'。引用計數的對象也不應該手動釋放,當最後一個強引用超出範圍時它們將自動釋放。 –

+0

不知道這是我想要的,因爲我試圖使用接口而不是實例。但是,無論如何,我要麼像@fantaghirocco那樣改變句子, – Izuel

1

你的例子有點奇怪。

由於您已聲明TBase構造函數爲virtual abstract,因此必須在TExample類中聲明構造函數。

正如其他人所說的,您必須將override指令添加到您的析構函數中。

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. 

,看起來怪我被釋放在基地析構函數FADO對象,因爲他們是在派生類中創建了另一件事。手動

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; 
+0

我剛剛意識到你說的構造函數和析構函數 – Izuel

+0

順便說一句,你給了我CoInitialize(零)和CoUninitialize解決方案之前,我收到錯誤...謝謝 – Izuel

+0

抱歉,CoI /庫爾是我的測試在控制檯的一部分應用程序:如果您不使用控制檯應用程序,則不需要它們。此外,類的構造函數或析構函數不是插入此類代碼的好地方:D。無論如何,這只是ADO所要求的 – fantaghirocco

4

不要隨意接口對象:


作爲一個方面說明,我更喜歡設計這個樣子!

當一個類從TInterfacedObject然後它自動引用計數派生並因爲沒有接口參考到它離開將被立即自動釋放。

這是什麼意思?

以下過程在其接口IBase上引用TExample的實例。當從堆棧中刪除Obj變量時,會自動清除內存。

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 

下一個程序在它的類名引用的TExample一個實例。這裏的參考計數是無效的。編譯器不包含對_AddRef_Release的調用。所以此過程泄漏內存:

procedure Foo; 
var 
    Obj: TExample; 
begin 
    Obj := TExample.Create; 
    Obj.test; 
end; // Obj will not be freed automatically 

所以,你會需要清理堆在你自己是這樣的:

procedure Foo; 
var 
    Obj: TExample; 
begin 
    Obj := TExample.Create; 
    try 
    Obj.test;   
    finally 
    Obj.Free; 
    end; 
end; 

它的工作原理,但是當Obj圍繞通過它可以危險 。只要對象的引用存儲在接口引用中。讓我們來看看:

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; 

這裏會發生什麼?

Obj被創建並存儲爲類引用。引用計數爲0.當調用Bar(Obj)時,對象將存儲在接口引用中。當調用Bar時,編譯器包括對_AddRef_Release的調用。參考計數器將增加和減少,因此它再次變爲0,並且對象破壞自身。

如何處理?

  • 不要手動釋放接口對象!
  • 讓引用計數爲你做的工作。
  • 不要按其具體的類來存儲TInterfacedObject的派生值。
  • 僅通過它們實現的接口來存儲它們。
  • 避免接口對象之間的循環引用。 Delphi中沒有垃圾收集器!