7

C++/CLI でオブジェクトを適切にクリーンアップする方法を見つけようとしています。

私はこれらの 2つの記事 ( 1、2 )を読んだり、ざっと目を通したりして、標準を調べ、他のいくつかの質問、特にこの質問を調べました。

私はさまざまな情報を持っています:

  1. ファイナライザーはアンマネージ リソースをクリーンアップする必要があります (そのため、オブジェクトがガベージ コレクションされるとすべてがクリーンアップされます。
  2. デストラクタは、マネージド リソースをクリーンアップし (Foo または Foo.Dispose()? を削除)、ファイナライザを呼び出す必要があります ( 1に従って) 。
  3. デストラクタとファイナライザの両方を複数回呼び出すことができます ( 8.8.8 の3ページ 26 の終わりを参照)。
  4. デストラクタが呼び出された場合、ファイナライザはもう呼び出されません ( 1に従って) (CLR によってではありません。つまり、まだ自分で呼び出すことができます)。
  5. デストラクタは基本クラスのデストラクタを呼び出します ( 3ページ 25 を参照)。
  6. ファイナライザを持つクラスには、常にデストラクタが必要です (おそらく、アンマネージ リソースを確定的にクリーンアップするため)。
  7. ファイナライザーへの呼び出しは、基本クラスのファイナライザーを呼び出しません ( 3 19.13.2 p. 131)

しかし、一部には次の事実によって引き起こされる多くの混乱もあります。

  1. ファイナライザーは C# ではデストラクタと呼ばれます
  2. デストラクタは内部的に Dispose メソッドと Finalize メソッドを生成しますが (Finalize については不明)、Finalize メソッドはファイナライザではありません
  3. C++ ではデストラクタのセマンティクスが異なり、一般的に決定論的なクリーンアップとガベージ コレクションの両方を持つことの複雑さ

答えとして私が望むのは、クラスに含まれる可能性のあるさまざまな種類のデータ (マネージド、アンマネージド、マネージドだが使い捨てなど、他に考えられるものは何でも) と適切に記述されたデストラクタとファイナライザを含むクラスの例です。

さらに具体的な質問が 2 つあります。

  1. bool hasBeenCleanedUpメンバーを持ち、デストラクタ/ファイナライザのコード全体を条件付きにするだけで、複数回呼び出される可能性に対処することは受け入れられますか?
  2. GC によってクリーンアップされている可能性があるため、デストラクタによってのみクリーンアップでき、ファイナライザではクリーンアップしてはならないデータはどれですか?
4

1 に答える 1

5

あなたの質問に対する完全な回答ではありませんが、長すぎてコメントに収まりません。

各オブジェクトがマネージド オブジェクトのみを参照するフル マネージドの世界では、唯一のリソースはメモリであり、GC がそれを処理するため、ファイナライザーやデストラクタは必要ありません。

管理されていないリソースを参照する場合、不要になったときに解放する責任があります。

そのため、専用のクリーンアップ コードを実装する必要があります。

2 つの可能性があります。

  • 管理されていないリソースが不要になったときに、クリーンアップ コードを決定論的に実行できることがわかります。これは、デストラクタ/Disposeを介して実装されます。

  • これらのリソースがいつ不要になるかわからないため、リソースをラップするオブジェクトが GC によって収集される最後の時点でクリーンアップを延期します。これはファイナライザーによって実装されます。

必要以上のメモリを消費せず、GC プロセスへの追加のオーバーヘッドを回避できるため、最初の状況の方がはるかに優れていると思います。

インスタンスの存続期間は使用状況によって異なる可能性があるため、通常は両方を実装します。

CLR レベルでは、確定的なクリーンアップなどはなく、ファイナライザーのみです。

言語/API レベルでは、決定論的なクリーンアップがサポートされています。

  • ネイティブ C++ では、スコープを終了するとき、または「削除」するときにデストラクタが呼び出されます。

  • .Net の世界では、Dispose パターンがあります

  • 純粋に管理された C++/CLI の世界では、デストラクタは Dispose にマップされます。

クリーンアップ コードをいつ実行できるかを正確に知る機会がある場合は、destructorを呼び出します (またはインフラストラクチャに呼び出しさせます) 。クリーンアップが完了したら、次の GC でオブジェクトをすぐに収集できるように、すべてのファイナライズ プロセスを取り除くことができます。

最初の一連のポイントについての説明:

  1. はい

  2. デストラクタは、管理されていないリソースのクリーンアップも担当します。クリーンアップ コードを因数分解した場所であれば、ファイナライザーを呼び出すことができます。

  3. 技術的には可能ですが、論理的には、単純なブール値ガードで防止する必要があります

  4. はい、すべてのクリーンアップを実行する必要があるため、オブジェクトをファイナライズしないように CLR に依頼します。

  5. はい、基本クラスは割り当てられたリソースを知っているためです

  6. はい、これは確定的なクリーンアップ用です

  7. そうであることを確認する必要があります

そして他の人:

  1. はい ~MyClass は Finalize メソッドのオーバーライドにマップされます

  2. 上記のように、デストラクタは Dispose にマップされますが、ファイナライザを自分で実装する必要があります: !MyClass

  3. 概要: C++ デストラクタと Dispose パターンは決定論的クリーンアップ用であり、C# デストラクタ、C++/CLI ファイナライザは GC によってトリガーされる非決定論的クリーンアップ用です。

于 2013-11-03T16:17:47.003 に答える