一部の言語(つまり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
- doSomethingElse、ブール値「false」を返すことができます(失敗した場合)
- doSomethingElseAgainは、Errorオブジェクト(__LINE __、__ FILE__、およびスタック変数の半分の両方を含む)を返すことができます。
- doTryToDoSomethingWithAllThisMess、まあ...上記の関数を使用して、タイプのエラーコードを返します...
呼び出された関数の1つが失敗した場合、doTryToDoSomethingWithAllThisMessが返されるタイプは何ですか?
リターンコードは普遍的な解決策ではありません
演算子はエラーコードを返すことはできません。C ++コンストラクターもできません。
戻りコードは、式を連鎖できないことを意味します
上記の点の当然の結果。書きたい場合はどうすればよいですか?
CMyType o = add(a, multiply(b, c)) ;
戻り値はすでに使用されているため、できません(場合によっては変更できないこともあります)。したがって、戻り値が最初のパラメータになり、参照として送信されます...またはそうではありません。
例外が入力されます
例外の種類ごとに異なるクラスを送信できます。リソースの例外(つまり、メモリ不足)は軽いはずですが、それ以外は必要なだけ重い可能性があります(Java例外がスタック全体を提供するのが好きです)。
その後、各キャッチを特殊化できます。
再スローせずに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.
}
(デバッグ時にのみアクティブになる同様のマクロがあります)。
本番環境では、構成ファイルが存在しないため、クライアントにこのマクロの結果が表示されることはありません...ただし、必要に応じて簡単にアクティブ化できます。
結論
リターンコードを使用してコーディングする場合、失敗に備えることになり、テストの要塞が十分に安全であることを願っています。
例外を使用してコーディングする場合、コードが失敗する可能性があることを知っており、通常、コード内の選択された戦略的位置にカウンターファイアキャッチを配置します。しかし、通常、あなたのコードは「それがしなければならないこと」よりも「私が恐れていることが起こる」ことに関するものです。
しかし、コーディングするときは、自由に使える最高のツールを使用する必要があり、「エラーを隠さないで、できるだけ早く表示する」という場合もあります。上で話したマクロはこの哲学に従います。