10年の練習。 パート2:リソース

こんにちは。 C ++でのリソース管理に関する大きな記事を書くつもりでした。

しかし実際には、このトピックは非常に複雑で多面的であるため、自分で使用する特定の手法について詳しく説明します。 この手法はあらゆる場合に役立つわけではありませんが、オブジェクトを操作する際に多くの時間と神経を節約できます。 ただし、広く知られていません。



このアプローチは「すべてがどこかに属している」と呼ばれます。 QtからMirekFídler率いる著者グループによって作成された素晴らしいU ++フレームワークに切り替えて、私は最初にそれについて知りました。 これは約5年前に行われたので、メソッド自体だけでなく、そのアプリケーションの経験に基づいた実用的なアドバイスも共有します。



要するに、テクニックの本質は「すべてが誰かのもの」というフレーズで表現できます。

ホストクラスには、完全な作業に必要なすべてのリソースが含まれています。 例:

ホッパーのノードを記述するオブジェクトは、ホッパーのクラスに含まれます。 複雑な計算クラスのデータオブジェクトと同様に、ヘルパーオブジェクトが含まれています。 などなど。

例外は、最も基本的なオブジェクトであるいくつかのグローバル変数です。 ある意味では、彼らは名前のないグローバルな「クラス」のメンバーです。



次のように言います:
  1. オブジェクトは、クラスの通常のメンバーとして定義されるか、クラスのメンバーであるコンテナーに配置されます
  2. 財産の所有権は譲渡されません


注:ポインターとしてではなく、リンクとしてではなく、通常の方法で定義されます。 オブジェクトへのポインタを取得し、どこかに渡すことができますが、使用目的のみです。 つまり、サードパーティのコードでは、ポインターを削除したり、ポインターを新規作成したりすることはできません。 ホストクラスによってのみ管理されます。





オブジェクトがすぐに作成されない場合はどうなりますか?

この場合、単一のコンテナー(unique_ptrなど)または配列コンテナーのいずれかが必要です。 その主な機能は、デストラクタ内のオブジェクトの自動削除です。 それだけです!



そして、ここで私たちは立ち止まって、得たものについて考えます。



1.手動呼び出しnew / deleteを削除しました。 そして、彼らはそれを非常にうまく取り除いたので、外れ値の例外や他の状況の場合でも、リソースは確実に削除されます。



2.プログラムのセマンティクスを考慮した非常に優れた自動リソース管理システムを取得しました。

たとえば、複数のウィンドウがあります。 プログラムの過程でウィンドウの一部が破壊されます。 これにより、すべてのリソースが削除されます。 リソースが削除される瞬間を正確に制御します。これは非常に重要です。

ガベージコレクターに依存しません。 適切なタイミングでメモリが解放されることは確かです。



決定論的なリソース管理は、いつでも(ビデオの再生中、またはサーボドライブを停止する必要があるなど)削除手順につまずく能力と、適切なタイミングでリソースを削除しようとして無駄にガベージコレクター振る能力との大きな対照であることを認める必要があります。



3.ホストオブジェクトがそのメンバーへのポインターを発行すると、このポインターによって、目的のクラスの必要なオブジェクトが存在することを保証できます。 このようなポインターを使用する場合、ホストオブジェクトのスコープで作業します。 つまり、ポインターの有効性が保証されます。 分かりますか? ポインターによるオブジェクトの存続期間は、コンパイラー自体によって保証されます。コンパイラーは、コンパイル段階でホストオブジェクトの可視性をチェックします。

それはほとんど夢です:コンパイル段階で有効性が確認されたポインターを通して効果的に作業すること! そして、これらすべて-オーバーヘッドなし。



4.最後に、作成、削除、およびアクセスの有効性の問題がオーバーヘッドなしで解決されたことに気付いたとき、参照カウンターと内部のより複雑なメカニズムを備えた「複雑な」スマートポインターは単に不要になるという結論に達しました。



プログラムの構造に制限を課しています。 より徹底的な設計と計画を実施します。 このため、オーバーヘッドのないリソースを使用して、より決定的な作業を行うことができます。 削除するすべての明示的な呼び出し、参照カウントを持つnewおよびcontainerへの明示的な呼び出しのほとんどを破棄し、それらに伴うすべての問題を回避します。



このようなプログラムは、Cのプログラムとは質的に異なります。ここでは、現在の機能に適用されないすべての範囲から隠しています。 ここでは、ほとんどの場合、数十のグローバル変数やオブジェクト、およびそれらを提供する数百のグローバル関数はありません。 代わりに、通常、いくつかのグローバルオブジェクト変数があります。 それらはそれぞれ非常に自律的で、含まれるオブジェクトによって実装される十分に大きな機能を実装します。 前回の記事で、これらのオブジェクトをprivateメンバーにすることが望ましいと述べました。 これにより、クラス階層内の連結が最小限に抑えられます。



今、要するに、アプローチは何ですか。



落とし穴について。

まず第一に、これは、参照カウンターでスマートポインターを積極的に使用する主流のアプローチと比較して、クラスへの完全に異なるスタイルの分解を意味します。 多くの場合、これらの制限を課すことは非常に困難であり、スマートポインターを使用したアプローチはより魅力的です。 しかし、フレームワークの開発者の実践と記事の著者の実践が示すように、アプローチに従ってすべてを試してみることをお勧めします。 これらの努力は、プログラムをデバッグおよびサポートするときに何十回も報います。



次に、積極的に使用するには、コピー演算子の形式で要素クラスに制限を課さない、優れた高速のコンテナが必要になります。 このようなコンテナの基本的な実装は非常に簡単です。 効果的な実装は別の記事です。 効果的な作業を行うには、 転送 (破壊的なコピーでもある)と転送 (超高速コピーでもある)を実装する必要があるため、興味深い場合は、これらのトピックについてさらに説明します。



第三に、これらのルールから次のGUI戦略が流れます。 コントロールは論理所有者に属します(ウィンドウではなく、所有者であるため、セマンティクスを格納する所有者に属します)。 この意味では、2つの選択肢があります。MVCの謝罪者は、物事をそのままにしておくことができます。 または、このアプローチを取ることができます。これは、実践が示すように、多くの場合、それ自体で対価を支払っています。 もちろん、これは神聖な牛ではありませんが、実際には批判的な考察と検証の対象となります。



不正確な点を指摘し、試して、批判し、適用してください。 フィードバックをお待ちしております。



参照:

1. U ++の概要



次のパートでは、ピック動作と移動可能なオブジェクトについて説明します。 これらの機能がオブジェクトの処理速度を大幅に向上させる方法を示します。 また、それらに基づくコンテナは、STLの4〜5倍高速です

その後、Erlangに触発された、マルチスレッドのターボ速度で非常に安全な実装に進むことができます。



All Articles