22
type
  TSomeRecord = Record
    field1: integer;
    field2: string;
    field3: boolean;
  End;
var
  SomeRecord: TSomeRecord;
  SomeRecAr: array of TSomeRecord;

これは私が持っているものの最も基本的な例であり、再利用したいのでSomeRecord(特定のフィールドが空のままで、すべてを解放せずに、再利用するときに一部のフィールドが引き継がれますがSomeRecord、これは明らかに望ましくありません)方法を探しています一度にすべてのフィールドを解放します。から始めてstring[255]使用しましZeroMemory()たが、メモリリークが発生するまでは問題ありませんでした。これは、に切り替えたためstringです。理由を知るにはまだ知識が不足していますが、動的であることに関連しているようです。私も動的配列を使用しているので、ZeroMemory()動的なものを試すとリークが発生すると思います。それを理解するのに1日が無駄になりました。Finalize()onSomeRecordまたはSomeRecArbeforeを使用してこれを解決したと思いますZeroMemory()、しかし、これが適切なアプローチなのか、それとも私が愚かなのかはわかりません。

問題は、すべてを一度に解放するにはどうすればよいかということです。私が気付いていないこのための単一の手順がまったく存在しますか?

別の言い方をすれば、そもそもこれらのレコードを別の方法で実装する方法を提案することもできます。そのため、ものを解放するために複雑な試みを行う必要はありません。New()でレコードを作成してから削除する方法を調べましたDispose()が、呼び出し後の変数Dispose()が nil ではなく未定義の場合の意味がわかりません。SomeRecord: TSomeRecordさらに、特定の型の変数 ( ) と型を指す変数( )の違いがわかりませんSomeRecord: ^TSomeRecord。上記の問題については現在調査中です。誰かがすぐに説明できない限り、時間がかかる可能性があります。

4

4 に答える 4

33

レコードへのメソッドの実装をサポートするバージョンの Delphi があると仮定すると、次のようにレコードをクリアできます。

type
  TSomeRecord = record
    field1: integer;
    field2: string;
    field3: boolean;
    procedure Clear;
  end;

procedure TSomeRecord.Clear;
begin
  Self := Default(TSomeRecord);
end;

コンパイラがサポートしていない場合Defaultは、次のように非常に簡単に同じことができます。

procedure TSomeRecord.Clear;
const
  Default: TSomeRecord=();
begin
  Self := Default;
end;

メソッド内の値の型を変更したくない場合があります。その場合、空のレコード値を返す関数を作成し、代入演算子とともに使用します。

type
  TSomeRecord = record
    // fields go here
    class function Empty: TSomeRecord; static;
  end;

class function TSomeRecord.Empty: TSomeRecord;
begin
  Result := Default(TSomeRecord);
end;

....

Value := TSomeRecord.Empty;

余談ですが、 のドキュメント参照が見つかりませんDefault(TypeIdentifier)。どこで見つけられるか知っている人はいますか?


あなたの質問の 2 番目の部分については、レコードを使用し続けず、動的配列を使用してそれらを割り当てない理由はないと思います。自分でライフタイムを管理しようとすると、エラーが発生しやすくなります。

于 2012-06-16T18:57:21.873 に答える
8

考えを過度に複雑にしないでください。

「デフォルト」を割り当てるrecordと、CPU パワーとメモリが失われるだけです。

arecordが a 内で宣言されるとTClass、ゼロで埋められるため、初期化されます。スタックに割り当てられると、参照カウントされた変数のみが初期化されます。他の種類の変数 (integer、double、boolean、enumerations など) はランダムな状態 (おそらく非ゼロ) になります。ヒープに割り当てられる場合、getmem何も初​​期化せず、allocmemすべてのコンテンツをゼロで埋め、new参照カウントされたメンバーのみを初期化します (スタックの初期化など): すべての場合でdispose、いずれかを使用finalize+freememして解放しますヒープ割り当てrecord

したがって、正確な質問については、あなた自身の仮定は正しかったです。使用後にレコードの内容をリセットするには、前の「」なしでfillchar「」(または「 」)を使用しないでください。正しくて最速の方法は次のとおりです。zeromemoryfinalize

Finalize(aRecord);
FillChar(aRecord,sizeof(aRecord),0);

繰り返しになりますが、デフォルト レコードを割り当てるよりも高速です。いずれの場合Finalizeでも、複数回使用しても、メモリ リークは発生しません - 100% 返金保証!

編集:によって生成されたコードを見るとaRecord := default(TRecordType)、コードは十分に最適化されていFinalizeます。そのため、構文がコピー/代入 ( ) であっても、コピー/代入として実装されていません。ここで私の間違い。stosdFillChar:=

しかし、 DelphiWebScript の dynamic arrays のように、Embarcadero が as syntaxのようなメソッドを:=使用するべきだった場合、 a を使用する必要があるという事実はまだ好きではありません。実際、この構文は C# で使用されているものとまったく同じです。Embacardero がどこでも C# 構文を模倣しているだけのように聞こえますが、これが変だとはわかりません。Delphi が単なる信奉者であり、実装が「その方法」を考えていない場合、ポイントは何ですか? 人々は常に元の C# を先祖よりも好むでしょう (Delphi の父は同じです)。recordaRecord.Clear:=

于 2012-06-16T20:41:59.407 に答える
6

私が考える最も簡単な解決策は次のとおりです。

const
  EmptySomeRecord: TSomeRecord = ();
begin
  SomeRecord := EmptySomeRecord;

ただし、質問の残りの部分すべてに対処するには、次の定義を使用してください。

type
  PSomeRecord = ^TSomeRecord;
  TSomeRecord = record
    Field1: Integer;
    Field2: String;
    Field3: Boolean;
  end;
  TSomeRecords = array of TSomeRecord;
  PSomeRecordList = ^TSomeRecordList;
  TSomeRecordList = array[0..MaxListSize] of TSomeRecord;    
const
  EmptySomeRecord: TSomeRecord = ();
  Count = 10;    
var
  SomeRecord: TSomeRecord;
  SomeRecords: TSomeRecords;
  I: Integer;
  P: PSomeRecord;
  List: PSomeRecordList;

procedure ClearSomeRecord(var ASomeRecord: TSomeRecord);
begin
  ASomeRecord.Field1 := 0;
  ASomeRecord.Field2 := '';
  ASomeRecord.Field3 := False;
end;

function NewSomeRecord: PSomeRecord;
begin
  New(Result);
  Result^.Field1 := 0;
  Result^.Field2 := '';
  Result^.Field3 := False;
end;

次に、それらを操作する方法に関する複数の例を次に示します。

begin
  // Clearing a typed variable (1):
  SomeRecord := EmptySomeRecord;

  // Clearing a typed variable (2):
  ClearSomeRecord(SomeRecord);

  // Initializing and clearing a typed array variabele:
  SetLength(SomeRecords, Count);

  // Creating a pointer variable:
  New(P);

  // Clearing a pointer variable:
  P^.Field1 := 0;
  P^.Field2 := '';
  P^.Field3 := False;

  // Creating and clearing a pointer variable:
  P := NewSomeRecord;

  // Releasing a pointer variable:
  Dispose(P);

  // Creating a pointer array variable:
  ReallocMem(List, Count * SizeOf(TSomeRecord));

  // Clearing a pointer array variable:
  for I := 0 to Count - 1 do
  begin
    Pointer(List^[I].Field2) := nil;
    List^[I].Field1 := 0;
    List^[I].Field2 := '';
    List^[I].Field3 := False;
  end;

  // Releasing a pointer array variable:
  Finalize(List^[0], Count);

お好みで選んだり、組み合わせたり。

于 2012-06-16T18:13:20.157 に答える
1

ではSomeRecord: TSomeRecordSomeRecordタイプのインスタンス/変数になりますTSomeRecordSomeRecord: ^TSomeRecordを使用SomeRecordすると、タイプ のインスタンスまたは変数へのポインターになりますTSomeRecord。最後のケースでSomeRecordは、型付きポインターになります。アプリケーションがルーチン間で大量のデータを転送したり、外部 API とやり取りしたりする場合は、型付きポインターが推奨されます。

new()型付きポインターでdispose()のみ使用されます。型付きポインターを使用すると、コンパイラーは、アプリケーションがこの種の変数で使用しているメモリを制御/認識できません。型付きポインタが使用するメモリを解放するのはあなた次第です。

一方、通常の変数を使用すると、使用と宣言に応じて、コンパイラはそれらが不要になったと判断したときに、それらによって使用されたメモリを解放します。例えば:

function SomeStaff();
var
    NativeVariable: TSomeRecord;
    TypedPointer: ^TSomeRecord;
begin
    NaviveVariable.Field1 := 'Hello World';

    // With typed pointers, we need to manually
    // create the variable before we can use it.
    new(TypedPointer);
    TypedPointer^.Field1 := 'Hello Word';

    // Do your stuff here ...

    // ... at end, we need to manually "free"
    // the typed pointer variable. Field1 within
    // TSomerecord is also released
    Dispose(TypedPointer);

    // You don't need to do the above for NativeVariable
    // as the compiler will free it after this function
    // ends. This apply also for native arrays of TSomeRecord.
end;

上記の例では、変数NativeVariableは関数内でのみ使用されるSomeStaffため、コンパイラは関数が終了すると自動的に変数を解放します。これは、配列やレコードの「フィールド」を含むほとんどのネイティブ変数に適用されます。オブジェクトの扱いは異なりますが、それは別の投稿に譲ります。

于 2012-06-16T19:27:10.577 に答える