2015-11-06 3 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; 

Мой вопрос, как я уничтожу этот Окс объект, который является IBase 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; в моем проекте есть некоторые утечки, и код никогда не достигает точки останова деструктора.

Я что-то не хватает ???

EDIT:

Я изменил мои конструкторы, теперь как:

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

Вам нужно добавить 'override;' в декларацию 'destructor'. Кроме этого, вам не нужно ничего делать, чтобы освободить объект. –

+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 (nil) и CoUninitialize, прежде чем я получил ошибку ... Спасибо – Izuel

+0

извините, CoI/CoU был частью моего теста в консоли app: они вам не нужны, если вы не используете консольное приложение. Более того, конструктор или деструктор класса не являются хорошими местами для вставки такого кода: D. Это было запрошено только ADO – fantaghirocco

4

не освободим сопрягаемые объекты вручную!

Когда класс является производным от TInterfacedObject, то он автоматически подсчета ссылок и будет освобожден автоматически, как только там нет ссылки интерфейса к нему влево.

Что это значит?

Следующая процедура ссылается на экземпляр TExample по его интерфейсу IBase. Он автоматически очищает память, когда переменная 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), объект будет храниться в ссылке на интерфейс. Компилятор включает вызовы _AddRef и _Release, когда вызывается Bar. Счетчик ссылок будет увеличен и уменьшен, поэтому он снова станет 0, и объект уничтожит себя.

Как с этим бороться?

  • Не производите сопрягаемые объекты вручную!
  • Позвольте отсчету ссылок выполнить работу за вас.
  • Не следует хранить производные от TInterfacedObject по его конкретному классу.
  • Храните их только через интерфейсы, которые они реализуют.
  • Избегайте циркулярных ссылок между сопряженными объектами. В Delphi нет сборщика мусора!