1

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++ 標準では、コンパイラが式の評価順序にある​​程度の自由度を与えることができます。これにより、コンパイラは、他の方法では不可能な最適化を実行できるからです。これを可能にするために、式の評価規則は例外セーフではない方法で指定されているため、例外セーフなコードを書きたい場合は、これらのケースについて知って回避する必要があります。(これを行う最善の方法については、以下を参照してください。)

だから私の質問は:

  1. この典型的な例外の安全でないコードを修正するにはどうすればよいですか? このようなコードを書くことを単に避けるべきでしょうか?

  2. 答えは私を少し混乱させました.コンストラクターの失敗を処理するために、C++ FAQに従ってコンストラクターから例外をスローし、割り当てられたメモリが適切に解放されることを確認する必要があります.上記のコードにまだ例外の安全性の問題がありますか?

お時間をいただき、ありがとうございました。

4

3 に答える 3

3

まず、次のように記述しますmake_unique

template<typename T, typename... Args>
std::unique_ptr<T> make_unique( Args&&... args ) {
  return {new T(std::forward<Args>(args)...)};
}

基本的に見落としとして、標準に含まれていませんでした。 std::make_sharedあり、make_uniqueおそらく C++14 または 17 で表示されます。

次に、関数のシグネチャを次のように変更します。

//  In some header file:
void f( std::unique_ptr<T1>, std::unique_ptr<T2> );

そしてそれを次のように呼び出します:

f( make_unique<T1>(), make_unique<T2>() );

その結果、例外セーフ コードが生成されます。

T1使用したい重要なコンストラクターがある場合は、引数をに渡すだけmake_unique<T1>で、それらを のコンストラクターに完全に転送できますT1

T1ビア()またはを構築する複数の方法には問題がありますが、{}完璧なものはありません。

于 2013-05-16T19:25:46.043 に答える
2

最初: はい、スレッド セーフの問題を回避します。

を書き換えられない場合は、次のことfを考慮してください。

auto t1 = std::make_unique<T1>(); //C++14
std::unique_ptr<T2> t2{new T2}; //C++11
f( t1.get(), t2.get() );        //or release(), depending on ownership policies of f

ただし、できる場合は、次のようにします。

void f(std::unique_ptr<T1>, std::unique_ptr<T2>);

//call:
f(make_unique<T1>(), make_unique<T2>());
于 2013-05-16T19:45:18.470 に答える