GOTW #56によると、次のコードには、潜在的な従来のメモリ リークと例外の安全性の問題があります。
// In some header file:
void f( T1*, T2* );
// In some implementation file:
f( new T1, new T2 );
その理由は、 we new T1
、またはnew T2
の場合、クラスのコンストラクターから例外がスローされる可能性があるためです。
一方、説明によると:
簡単な要約: 「new T1」のような式は、単純に new-expression と呼ばれます。new-expression が実際に何をするかを思い出してください (ここではあまり関係がないため、簡単にするために配置と配列の形式は無視します)。
メモリを割り当てます
そのメモリに新しいオブジェクトを構築します
例外のために構築が失敗した場合、割り当てられたメモリは解放されます
したがって、各 new 式は基本的に一連の 2 つの関数呼び出しです。1 回の operator new() への呼び出し (グローバルなもの、または作成されるオブジェクトの型によって提供されるもの) と、コンストラクターへの呼び出しです。
例 1 について、コンパイラが次のようにコードを生成することを決定した場合に何が起こるかを考えてみましょう。
1: T1 にメモリを割り当てる
2: T1 を構築する
3: T2 にメモリを割り当てる
4: T2 を構築する
5: f() を呼び出す問題は次のとおりです。ステップ 3 またはステップ 4 のいずれかが例外のために失敗した場合、C++ 標準では、T1 オブジェクトを破棄してメモリの割り当てを解除する必要はありません。これは典型的なメモリ リークであり、明らかに良いことではありません。[...]
続きを読む:
クリーンアップに関しては、コンパイラに正しいことを行うように要求することによって、標準が問題を防止しないのはなぜですか?
基本的な答えは、気づいていなかったということです。気づいた今でも、修正するのは望ましくないかもしれません。C++ 標準では、コンパイラが式の評価順序にある程度の自由度を与えることができます。これにより、コンパイラは、他の方法では不可能な最適化を実行できるからです。これを可能にするために、式の評価規則は例外セーフではない方法で指定されているため、例外セーフなコードを書きたい場合は、これらのケースについて知って回避する必要があります。(これを行う最善の方法については、以下を参照してください。)
だから私の質問は:
この典型的な例外の安全でないコードを修正するにはどうすればよいですか? このようなコードを書くことを単に避けるべきでしょうか?
答えは私を少し混乱させました.コンストラクターの失敗を処理するために、C++ FAQに従ってコンストラクターから例外をスローし、割り当てられたメモリが適切に解放されることを確認する必要があります.上記のコードにまだ例外の安全性の問題がありますか?
お時間をいただき、ありがとうございました。