9

ここで私はトリッキーな状況にあると思います。レコードのフィールドであるオブジェクトを解放できる必要があります。クラスの場合、通常はデストラクタでクリーンアップコードを記述します。しかし、レコードタイプは「デストラクタ」を導入できないため、TObject(Field).Freeを呼び出すことはどのように可能でしょうか。

私が予測する使用法には2つのタイプがあります。

  1. レコードを新しいものと交換します。

    この使い方は簡単に実装できると思います。レコードは値型であり、代入時にコピーされるため、代入演算子をオーバーロードして、古いレコードが所有するオブジェクトを解放できます。

    編集:割り当てのオーバーロードはできませんでした。それは私にとって新しい情報です。。)

  2. レコード変数が定義されたスコープを終了します。

    オブジェクトを解放するプライベートメソッドを考えることができます。このメソッドは、スコープの励起時に手動で呼び出すことができます。しかし、ここに同じ質問があります:それをより記録的にする方法は?この振る舞いはクラスのように感じます...

これがサンプルです(そして明らかに意図された使用法ではありません):

TProperties = record
  ... some other spesific typed fields: Integers, pointers etc..
  FBaseData: Pointer;

  FAdditionalData: TList<Pointer>;
  //FAdditionalData: array of Pointer; this was the first intended definition
end;

推定、

FAdditionalData:=TList<Pointer>.Crete;

レコードコンストラクタで呼び出されるか、レコード変数スコープで手動で呼び出されます。

procedure TFormX.ButtonXClick(Sender: TObject);
var
  rec: TProperties;
begin
  //rec:=TProperties.Create(with some parameters);

  rec.FAdditionalData:=TList<Pointer>.Create;

  //do some work with rec
end;

ButtonClickスコープを終了した後、recはなくなりましたが、TListはその存在を維持しているため、メモリリークが発生します...

4

2 に答える 2

12

レコードにオブジェクト参照しかない場合は、コンパイラに助けてもらうことはできません。あなたはそのオブジェクトの存続期間を単独で担当しています。代入演算子をオーバーロードすることはできず、スコープのファイナライズの通知はありません。

ただし、できることは、オブジェクトの有効期間を管理するガード インターフェイスを追加することです。

TMyRecord = record
  obj: TMyObject;
  guard: IInterface;
end;

TMyObjectが参照カウントによってその有効期間を管理していることを確認する必要があります。たとえば、 から派生しTInterfacedObjectます。

レコードを初期化するときは、次のようにします。

rec.obj := TMyObject.Create;
rec.guard := rec.obj;

この時点でguard、レコードのフィールドがオブジェクトの有効期間を管理します。

実際、この考え方をさらに推し進めたい場合は、専用のクラスを作成してオブジェクトの存続期間を保護することができます。IInterfaceこれにより、クラスに実装する必要がなくなります。Web 上には、この手法を説明する例がたくさんあります。たとえば、Jarrod Hollingworth のSmart Pointersというタイトルの記事と、Barry Kelly のReference-counted pointers, revisitedというタイトルの記事を提供します。他にもたくさんあります。それは古いトリックです !

ただし、ここにあるのは、値型と参照型の奇妙なハイブリッドであることに注意してください。表面的には、レコードは値型です。ただし、これは参照型のように機能します。値の型である他のフィールドがレコードにある場合、それはさらに混乱を招きます。このようなレコードを扱うときは、この問題に十分注意する必要があります。

一見すると、あなたの設計について詳しく知ることなく、レコードにオブジェクト参照を入れないようにアドバイスしたくなるでしょう。それらは、参照型、つまりクラス内により適しています。

于 2012-10-12T14:41:59.677 に答える
3

誰かが TLifetimeWatcher という名前のクラスを作成したことを覚えています。基本的には、次のようになります。

TLifetimeWatcher = class(TInterfacedObject)
private
  fInstance: TObject;
  fProc: TProc; 
public
  constructor Create(instance: TObject); overload;
  constructor Create(instance: TObject; proc: TProc); overload;
  destructor Destroy; override;
end;

// (クリーンアップ) プロシージャは、割り当てられている場合はデストラクタで実行されます。割り当てられていない場合は、Free メソッドを呼び出してインスタンスを解放します。

于 2012-10-12T14:59:44.023 に答える