一部の言語 (C++ など) では、リソース リークは理由になりません。
C++ は RAII に基づいています。
失敗、リターン、またはスローする可能性のあるコード (つまり、ほとんどの通常のコード) がある場合は、ポインターをスマート ポインター内にラップする必要があります (スタック上にオブジェクトを作成しない十分な理由があると仮定します)。
戻りコードはより詳細です
それらは冗長であり、次のようなものになる傾向があります。
if(doSomething())
{
if(doSomethingElse())
{
if(doSomethingElseAgain())
{
// etc.
}
else
{
// react to failure of doSomethingElseAgain
}
}
else
{
// react to failure of doSomethingElse
}
}
else
{
// react to failure of doSomething
}
結局のところ、コードは識別された命令の集まりです (この種のコードは製品コードで見ました)。
このコードは次のように変換できます。
try
{
doSomething() ;
doSomethingElse() ;
doSomethingElseAgain() ;
}
catch(const SomethingException & e)
{
// react to failure of doSomething
}
catch(const SomethingElseException & e)
{
// react to failure of doSomethingElse
}
catch(const SomethingElseAgainException & e)
{
// react to failure of doSomethingElseAgain
}
これは、コードとエラー処理を明確に分離するものであり、これは良いことです。
戻りコードはより脆い
1 つのコンパイラからのあいまいな警告 (「phjr」のコメントを参照) ではない場合、それらは簡単に無視できます。
上記の例で、誰かが考えられるエラーを処理するのを忘れたと仮定します (これは起こります...)。「返された」場合、エラーは無視され、後で爆発する可能性があります (つまり、NULL ポインター)。例外なく同じ問題は発生しません。
エラーは無視されません。爆発させたくない場合もありますが…慎重に選ばなければなりません。
リターン コードを変換する必要がある場合がある
次の関数があるとします。
- NOT_FOUND_ERROR と呼ばれる int を返すことができる doSomething
- bool "false" (失敗した場合) を返すことができる doSomethingElse
- doSomethingElseAgain は、Error オブジェクトを返すことができます (__LINE__、__FILE__、およびスタック変数の半分の両方を使用)。
- doTryToDoSomethingWithAllThisMess これは、まあ...上記の関数を使用して、タイプのエラーコードを返します...
呼び出された関数の 1 つが失敗した場合の doTryToDoSomethingWithAllThisMess の戻り値の型は何ですか?
リターン コードは普遍的なソリューションではありません
オペレーターはエラー コードを返すことができません。C++ コンストラクターもできません。
リターンコードは、式を連鎖できないことを意味します
上記のポイントの当然の結果。私が書きたいとしたらどうしますか:
CMyType o = add(a, multiply(b, c)) ;
戻り値が既に使用されているため、できません (変更できない場合もあります)。したがって、戻り値が最初のパラメーターになり、参照として送信されます...またはそうではありません。
例外が入力されます
例外の種類ごとに異なるクラスを送信できます。リソース例外 (つまり、メモリ不足) は軽くする必要がありますが、それ以外は必要に応じて重くすることができます (スタック全体を提供する Java 例外が好きです)。
その後、各キャッチを特殊化できます。
再スローせずに catch(...) を使用しないでください
通常、エラーは非表示にしないでください。再スローしない場合は、少なくとも、エラーをファイルに記録するか、メッセージボックスを開くか、何でも...
例外は... NUKE
例外の問題は、それらを使いすぎると、try/catch でいっぱいのコードが生成されることです。しかし、問題は別の場所にあります。誰が STL コンテナーを使用して自分のコードを試したりキャッチしたりするのでしょうか? それでも、これらのコンテナーは例外を送信できます。
もちろん、C++ では、例外をデストラクタから出さないでください。
例外は... 同期
スレッドがひざまずいたり、Windows メッセージ ループ内で伝播したりする前に、必ずそれらをキャッチしてください。
解決策はそれらを混合することでしょうか?
だから、解決策は、何かが起こってはならないときにスローすることだと思います。何かが発生する可能性がある場合は、戻りコードまたはパラメーターを使用して、ユーザーがそれに反応できるようにします。
ですから、唯一の質問は、「起こってはならないことは何ですか?」ということです。
それはあなたの関数の契約に依存します。関数がポインターを受け入れるが、ポインターが非 NULL でなければならないことを指定している場合、ユーザーが NULL ポインターを送信したときに例外をスローしても問題ありません (問題は、C++ では、関数の作成者が代わりに参照を使用しなかった場合です)。ポインターの、しかし...)
別の解決策は、エラーを表示することです
エラーが発生したくないことが問題になる場合があります。例外またはエラー リターン コードを使用するのはクールですが、それについて知りたいと考えています。
私の仕事では、一種の「アサート」を使用します。デバッグ/リリース コンパイル オプションに関係なく、構成ファイルの値に応じて、次のようになります。
- エラーを記録する
- 「ねえ、問題があります」というメッセージボックスを開きます
- 「ねえ、問題があります。デバッグしますか」というメッセージボックスを開きます
開発とテストの両方で、これにより、ユーザーは問題が検出されたときに正確に特定することができます。
従来のコードに簡単に追加できます。例えば:
void doSomething(CMyObject * p, int iRandomData)
{
// etc.
}
次のような種類のコードを導きます。
void doSomething(CMyObject * p, int iRandomData)
{
if(iRandomData < 32)
{
MY_RAISE_ERROR("Hey, iRandomData " << iRandomData << " is lesser than 32. Aborting processing") ;
return ;
}
if(p == NULL)
{
MY_RAISE_ERROR("Hey, p is NULL !\niRandomData is equal to " << iRandomData << ". Will throw.") ;
throw std::some_exception() ;
}
if(! p.is Ok())
{
MY_RAISE_ERROR("Hey, p is NOT Ok!\np is equal to " << p->toString() << ". Will try to continue anyway") ;
}
// etc.
}
(デバッグ時にのみアクティブな同様のマクロがあります)。
本番環境では構成ファイルが存在しないため、クライアントはこのマクロの結果を見ることはありません...しかし、必要なときに簡単にアクティブ化できます。
結論
リターン コードを使用してコードを作成するときは、失敗に備えることになり、テストの要塞が十分に安全であることを願っています。
例外を使用してコーディングする場合、コードが失敗する可能性があることを知っており、通常はコード内の選択された戦略的な位置にカウンターファイア キャッチを配置します。しかし通常、あなたのコードは「何が起こるか」よりも「何をしなければならないか」について書かれています。
しかし、コードを作成するときは、自由に使える最適なツールを使用する必要があり、「エラーを隠すのではなく、できるだけ早く表示する」ことが必要な場合もあります。上で話したマクロは、この哲学に従っています。