2015-09-15 12 views
11

なぜこのプログラムはメモリリークを報告しますか?TObjectListから継承する方法TObjectListから継承する代わりに<T>

{$APPTYPE CONSOLE} 

uses 
    System.Generics.Collections; 

type 
    TDerivedGenericObjectList = class(TObjectList<TObject>) 
    public 
    constructor Create; 
    end; 

constructor TDerivedGenericObjectList.Create; 
begin 
    inherited; 
end; 

var 
    List: TDerivedGenericObjectList; 

begin 
    ReportMemoryLeaksOnShutdown := True; 
    List := TDerivedGenericObjectList.Create; 
    List.Add(TObject.Create); 
    List.Free; 
end. 
+1

リファクタリングが役立ちます。 – smooty86

+1

コードに欠陥があるため、漏れがあります。あなたのコードは見えません。いずれにしても、なぜサブクラス化するのか。 'TObjectList 'を直接使うことができます。あなたがそれにメソッドを追加しない限り。 –

+0

@DavidHeffernanコンストラクタと使い方を追加して質問を編集しました。そのコードは 'TObjectList'で漏れません。しかし、 'TMembers'宣言を' TMembers = class(TObjectList ) 'に変更すると、上のコードはメモリリークを示しています。そして、はい、私はそれにメソッドを追加したいと思います。私がここに示しているのは、うまくいけば明確な質問のための単純な構成例です。 – alondono

答えて

13

。それは実際にTList<T>のコンストラクタであり、そこからTObjectList<T>が派生したクラスです。

TObjectList<T>で宣言されたすべてのコンストラクタは、プロパティを初期化するために使用されるAOwnsObjectsというパラメータを受け入れます。そのコンストラクタをバイパスしているため、OwnsObjectsはデフォルトでFalseになり、リストのメンバーは破棄されません。

OwnsObjectsを初期化するTObjectList<T>のコンストラクタを呼び出す必要があります。例えば:

type 
    TDerivedGenericObjectList = class(TObjectList<TObject>) 
    public 
    constructor Create(AOwnsObjects: Boolean = True); 
    end; 

constructor TDerivedGenericObjectList.Create(AOwnsObjects: Boolean); 
begin 
    inherited Create(AOwnsObjects); 
end; 

または::だから

type 
    TDerivedGenericObjectList = class(TObjectList<TObject>) 
    public 
    constructor Create(AOwnsObjects: Boolean = True); 
    end; 

constructor TDerivedGenericObjectList.Create(AOwnsObjects: Boolean); 
begin 
    inherited; 
end; 

、あなたが疑問に思うかもしれません

{$APPTYPE CONSOLE} 

uses 
    System.Generics.Collections; 

type 
    TDerivedGenericObjectList = class(TObjectList<TObject>) 
    public 
    constructor Create; 
    end; 

constructor TDerivedGenericObjectList.Create; 
begin 
    inherited Create(True); 
end; 

var 
    List: TDerivedGenericObjectList; 

begin 
    ReportMemoryLeaksOnShutdown := True; 
    List := TDerivedGenericObjectList.Create; 
    List.Add(TObject.Create); 
    List.Free; 
end. 

おそらく、より良い変形は、あなたのコンストラクタもAOwnsObjectsパラメータを提供させることであろう元のバージョンがのものではなくTList<T>のコンストラクタを選んだ理由。さて、それをもっと詳しく見てみましょう。ここにあなたのコードは、再びです:inheritedがこのように使用されている

type 
    TDerivedGenericObjectList = class(TObjectList<TObject>) 
    public 
    constructor Create; 
    end; 

constructor TDerivedGenericObjectList.Create; 
begin 
    inherited; 
end; 

は、コンパイラはこの1とまったく同じシグネチャを持つコンストラクタを探します。彼らはすべてパラメータを持っているので、TObjectList<T>で見つけることができません。それはTList<T>にあるものを見つけることができます、そして、それはそれが使用するものです。

次のバリアントが漏れないコメントで言及したよう:

constructor TDerivedGenericObjectList.Create; 
begin 
    inherited Create; 
end; 

この構文、裸inheritedとは対照的で、デフォルトのパラメータが置換されている場合に一致する方法を見つけるだろう。したがって、TObjectList<T>の単一パラメータコンストラクタが呼び出されます。

documentationは、この情報を持っています

継承された予約語が多型の挙動を実装する際の特別な役割を果たしています。メソッド定義の後に識別子がある場合とない場合があります。

メンバーの名前が続く場合は、参照されているメンバーの検索が、囲むメソッドのクラスの直下の祖先で始まる点を除き、通常のメソッド呼び出しまたはプロパティまたはフィールドへの参照を表します。例えば、次の場合:

inherited Create(...); 

は、定義されたメソッドの中で発生し、継承されたCreateを呼び出します。

継承されたメソッドの後に識別子がない場合、継承するメソッドと同じ名前を持つ継承されたメソッド、または同じメッセージの継承されたメッセージハンドラへのメッセージハンドラの場合はメッセージハンドラを参照します。この場合、inheritedは明示的なパラメーターを取りませんが、囲まれたメソッドが呼び出されたのと同じパラメーターを継承したメソッドに渡します。例:

inherited; 

は、コンストラクタの実装で頻繁に発生します。これは子孫に渡されたものと同じパラメーターを持つ継承コンストラクターを呼び出します。

+0

私はトップソリューションを試しましたが、実際にはTrueパラメータも削除されています。だから、 '継承されたCreate;だけが必要です。 – alondono

+2

しかし明示する方が良いです。私のバージョンの 'TObjectList 'は、呼び出し元にOwnsObjectを渡すよう強制します。 –

1

ジェネリックを使用できます。タイプキャスティングやメモリーリーク(TObjectList<T>またはTObjectDictionary<T>のリストは、フリーコマンドで内部オブジェクトを自動的に破壊する)なしでは正常に動作します。

いくつかのヒント:

  • TObjectList<TPerson>からmembersList.Freeのような無料の自動人物のリストを破壊します。

  • TList<TPerson> - 人物リストを破壊しないでください。デストラクタを作成し、リスト内のすべての人を手動で解放する必要があります。ここで

はあなたのコードの例である(新しいコンストラクタ、ないメモリリークを使用して、古いコードとの後方互換性を持つ - GetPersonを参照してください):あなたはTObjectList<T>のパラメータなしのコンストラクタを呼び出している

type 
     TPerson = class 
     public 
     Name: string; 
     Age: Integer; 

     function Copy: TPerson; 
     end; 

     TMembers = class(TObjectList<TPerson>) 
     private 
     function GetPerson(i: Integer): TPerson; 
     public 
     property Person[i: Integer]: TPerson read GetPerson; 

     constructor Create(SourceList: TMembers); overload; 
     end; 


    { TPerson } 

    function TPerson.Copy: TPerson; 
    var 
     person: TPerson; 
    begin 
     person := TPerson.Create; 
     person.Name := Self.Name; 
     person.Age := Self.Age; 
     Result := person; 
    end; 

    { TMembers } 

    constructor TMembers.Create(SourceList: TMembers); 
    var 
     person: TPerson; 
    begin 
     inherited Create; 

     for person in SourceList do 
     begin 
     Self.Add(person.Copy); 
     end; 
    end; 

    function TMembers.GetPerson(i: Integer): TPerson; 
    begin 
     Result := Self[i]; 
    end; 

    procedure TForm21.Button1Click(Sender: TObject); 
    var 
     person: TPerson; 
     memsList1: TMembers; 
     memsList2: TMembers; 
    begin 
     // test code 

     memsList1 := TMembers.Create; 

     person := TPerson.Create; 
     person.Name := 'name 1'; 
     person.Age := 25; 
     memsList1.Add(person); 

     person := TPerson.Create; 
     person.Name := 'name 2'; 
     person.Age := 27; 
     memsList1.Add(person); 

     memsList2 := TMembers.Create(memsList1); 

     ShowMessageFmt('mems 1 count = %d; mems 2 count = %d', [memsList1.Count, memsList2.Count]); 

     FreeAndNil(memsList1); 
     FreeAndNil(memsList2); 
    end; 
+0

**なぜ** memleaksがないのかを記述してください。コードだけでは理解できません。 –

+0

メモリリークレポートを有効にするには、 'Application.Initialize'の前にプロジェクトソースファイルの' ReportMemoryLeaksOnShutdown:= True; 'を追加します。メモリリークがあると、アプリケーション終了後にレポートが表示されます。 – JayDi

+0

それはあなたがどのようにmemleaksがないのですか? –