C++11 のチェーンされた委譲コンストラクターは、C++03 の init 関数スタイルよりも多くのオーバーヘッドを引き起こします!
C++11 標準ドラフトN3242のセクション 15.2 を参照してください。委譲チェーン内の任意のリンクの実行ブロックで例外が発生する可能性があり、C++11 はそれを考慮して既存の例外処理動作を拡張します。
[テキスト] と強調します。
初期化または破棄が例外によって終了する保存期間のオブジェクトでは、完全に構築されたすべてのサブオブジェクトに対してデストラクタが実行されます...つまり、プリンシパル コンストラクタ (12.6.2) が実行を完了し、デストラクタはまだ実行を開始していません。同様に、オブジェクトの非委任コンストラクターが実行を完了し、そのオブジェクトの委任コンストラクターが例外で終了した場合、オブジェクトの [上記のようにサブオブジェクトのように扱われる] デストラクタが呼び出されます。
これは、委任された ctor の C++ オブジェクト スタック モデルとの一貫性を説明しているため、必然的にオーバーヘッドが発生します。
ハードウェア レベルでスタックがどのように機能するか、スタック ポインターとは何か、自動オブジェクトとは何か、スタックの巻き戻しとは何かなどをよく理解して、これがどのように機能するかを理解する必要がありました。技術的には、これらの用語/概念は実装で定義された詳細であるため、N3242 はこれらの用語を定義していません。しかし、それはそれらを使用します。
その要点: スタック上で宣言されたオブジェクトがメモリに割り当てられ、実行可能ファイルがアドレス指定とクリーンアップを処理します。スタックの実装は C では単純でしたが、C++ では例外があり、C のスタック巻き戻しの拡張が必要です。Stroustrup * による論文のセクション 5では、拡張されたスタックの巻き戻しの必要性と、そのような機能によって導入される必要な追加のオーバーヘッドについて説明しています。
ローカル オブジェクトにデストラクタがある場合、そのデストラクタをスタックの巻き戻しの一部として呼び出す必要があります。[自動オブジェクトのスタック巻き戻しの C++ 拡張が必要] ...(ハンドラーを確立する標準的なオーバーヘッドに加えて) 最小限のオーバーヘッドのみを含む実装手法。
委任チェーン内のすべてのリンクのコードに追加するのは、まさにこの実装手法とオーバーヘッドです。すべてのスコープには例外の可能性があり、すべてのコンストラクターには独自のスコープがあるため、チェーン内のすべてのコンストラクターはオーバーヘッドを追加します (追加のスコープを 1 つだけ導入する init 関数と比較して)。
オーバーヘッドが最小限であることは事実です。適切な実装では、単純なケースを最適化してそのオーバーヘッドを取り除くことができると確信しています。ただし、5 つのクラスの継承チェーンがある場合を考えてみましょう。これらのクラスのそれぞれに 5 つのコンストラクターがあり、各クラス内で、これらのコンストラクターが連鎖して相互に呼び出し、冗長なコーディングを削減するとします。最も派生したクラスのインスタンスをインスタンス化すると、上記のオーバーヘッドが最大25回発生しますが、C++03 バージョンではそのオーバーヘッドが最大10回発生します。回。これらのクラスを仮想化して多重継承すると、これらの機能の蓄積に関連してこのオーバーヘッドが増加し、それらの機能自体が追加のオーバーヘッドを導入します。ここでの教訓は、コードがスケーリングされるにつれて、この新しい機能の噛み付きを感じるということです。
* Stroustrup リファレンスは、C++ 例外処理に関する議論を促進し、潜在的な (必ずしもそうではない) C++ 言語機能を定義するために、ずっと前に書かれました。人間が判読でき、「移植可能」であるため、実装固有のリファレンスよりもこのリファレンスを選択しました。この論文の主な用途はセクション 5 です。具体的には、C++ スタックの巻き戻しの必要性と、そのオーバーヘッドの発生の必要性についての議論です。これらの概念は論文内で正当化されており、現在 C++11 で有効です。