オブジェクトの有効期間とリソースの管理 (Modern C++)
マネージ言語とは異なり、C++ プログラムの実行時に、自動的になし-長い-使用するメモリ リソースを解放するガベージ コレクション (GC) ではありません。C++ では、リソース管理はオブジェクトの有効期間に直接関連します。このドキュメントでは、C++ のオブジェクトの有効期間とそれを管理する方法に影響する要因についてを説明します。
主に、非メモリ リソースを処理できないために、C++ GC がありません。のみ決定的デストラクターと同様に、C++ の非メモリ リソースが均等に処理できます。GC はメモリと CPU の消費量、局所性の高いオーバーヘッドなど、その他の問題もあります。Universality は巧妙な最適化を軽減することはできませんが根本的な問題です。
概念
重要なはオブジェクトの有効期間の管理をカプセル化されて、誰がオブジェクトを使用しているどのようなオブジェクトのリソースを所有している、またはそれらの rid を取得する方法またはもかどうかは、リソースをまったく所有を理解する必要はありません。だけで、オブジェクトを破棄するがあります。C++ コア言語オブジェクトを正しい時間には、破棄されたことを確認するために設計されていますようにブロックを逆の順序での建設終了です。オブジェクトが破棄されるとき、そのベースとメンバーを特定の順序では破棄されます。ヒープ割り当てまたは新しい配置のような特別なものの操作を行いますしない限り、オブジェクトは、言語が自動的に破棄されます。たとえば、 スマート ポインター のようなunique_ptrとshared_ptrと、標準テンプレート ライブラリ (STL) のコンテナーのようなvector、カプセル化new/deleteとnew[]/delete[]デストラクターあるオブジェクトでは。その理由は、スマート ポインター、STL コンテナーを使用することが重要です。
[有効期間の管理のもう 1 つの重要な概念: デストラクター。デストラクターはリリースのリソースをカプセル化します。(ニーモニックを一般的に使用される RRID、リソースのリリースは破棄です)。リソースを使用すると、「システム」から取得し、後で戻すことです。メモリは、最も一般的なリソースですがまたファイル、ソケット、テクスチャ、および他の非メモリ リソース。"リソースを所有している"つまり必要が操作を終了したら、それを解放する必要がも使用できます。オブジェクトが破棄されるときは、そのデストラクターが所有しているリソースを解放します。
最終的な概念は、DAG (送信非循環のグラフ) です。DAG の所有権のプログラム構造を形成します。オブジェクトが所有しない-はありませんのみできなくがまた本質的に無意味な。しかし、2 つのオブジェクトは、3 つ目のオブジェクトの所有権を共有できます。次のような DAG のいくつかの種類のリンクがあります: A は B のメンバー (B 所有 A) C ストア、 vector<D> (C 所有 D の各要素) E ストア、 shared_ptr<F> (E 共有所有権の F、可能性がありますその他のオブジェクト) など。(の代わりに生のポインター、ハンドル、またはその他のメカニズム)、デストラクター サイクルがないし、DAG 内のすべてのリンクがオブジェクトによって表される場合に限り、言語、ためリソース リークが可能です。すぐに、ガベージ コレクターを実行せず不要するとリソースが解放されます。有効期間の追跡をオーバーヘッドのないスタックのスコープ、ベース、メンバー、および関連の場合は、安価なのですshared_ptr。
ヒープ ベースの有効期間
ヒープ オブジェクトの有効期間を使用してスマート ポインター。使用shared_ptrとmake_sharedとして、既定のポインター アロケーター。使用weak_ptrサイクルを中断、キャッシュ、およびオブジェクトの有効期間について何も前提に影響を与えたりせずに観察します。
void func() {
auto p = make_shared<widget>(); // no leak, and exception safe
...
p->draw();
} // no delete required, out-of-scope triggers smart pointer destructor
使用unique_ptrの独自の所有権などで、 pimpl イディオム。(「コンパイル時のカプセル化の Pimpl (Modern C++)」を参照)。確認、 unique_ptrプライマリ ・ ターゲットのすべての明示的なnew式。
unique_ptr<widget> p(new widget());
非所有者と観察の生のポインターを使用できます。オーナーではないポインターを dangle 可能性があります、しかし、リークが発生することはできません。
class node {
...
vector<unique_ptr<node>> children; // node owns children
node* parent; // node observes parent, which is not a concern
...
};
node::node() : parent(...) { children.emplace_back(new node(...) ); }
パフォーマンスの最適化が必要な場合は、使用する必要があります通常ポインターとを削除するのには、明示的な呼び出しを所有します。例は、独自の低レベルのデータ構造を実装する場合です。
スタック ベースの有効期間
C++ は、最近のスコープのスタックに基づく 自動結合するために堅牢なコードを記述する優れた手段です スタックの有効期間 と データ メンバーの有効期間高効率-追跡の有効期間は、オーバーヘッドの基本的にフリーです。ヒープ オブジェクトの有効期間は diligent の手動で管理する必要があり、ソースのリソース リークが発生し非効率性、特に、生ポインターを作業している場合ことがあります。このコードは、スタック ベースのスコープを示していますを考慮してください。
class widget {
private:
gadget g; // lifetime automatically tied to enclosing object
public:
void draw();
};
void functionUsingWidget () {
widget w; // lifetime automatically tied to enclosing scope
// constructs w, including the w.g gadget member
…
w.draw();
…
} // automatic destruction and deallocation for w and w.g
// automatic exception safety,
// as if "finally { w.dispose(); w.g.dispose(); }"
静的の有効期間を控えめに使用して (グローバル静的、関数のローカル静的) ため、問題が発生することができます。グローバル オブジェクトのコンス トラクターが例外をスローした場合通常、アプリケーション障害をデバッグするは難しいことができる方法では。建設の順序は、静的の有効期間のオブジェクトの場合は、問題があるし、同時実行制御-セーフではありません。オブジェクトの構造問題であるだけでなく破棄順序は複雑な特にポリモーフィズムを使用するとします。オブジェクトまたは変数"ポリモフィック型"ではなく、複雑な構築/破棄の順序がない場合でも、スレッド セーフな同時実行の問題ですありますが。マルチ スレッドのアプリケーションは安全にスレッド ローカル ストレージ、リソースのロック、およびその他の特別な予防策をしなくても静的オブジェクト内のデータを変更できません。