1

デバッグ/リリース モードでアサートの失敗に対処する正しい方法または受け入れられている方法はありますか?

例: std::vector を返す関数があるとします。

返されたベクトルの長さが別のオブジェクトの長さと等しいことを期待して、これを行います。

std::vector<int> v = get_stuff();
ASSERT(v.size() == this->size() )
a = v[this->size() - 1 ];

アサーションがトリガーされない場合、このコードは正常に機能し、デバッグではクラッシュする可能性がありますが、少なくとも何かが間違っていることを警告するアサーションの失敗があります。問題は、サイレント クラッシュが発生するリリース モードにあります。これは、リリース コードでこのエラーを確認してから処理する必要があるということですか? それは可能ですが、アサーションの失敗を追加しても意味がありません。

4

5 に答える 5

2

anassertは、あなたが思っているのとは違う意味を持っています。それは例外の代わりではありません。それはあなたの考え方だと思います。デバッグの早い段階で問題を発見できるようにするためです。デバッグでアサートを取得した場合は、それを修正します。次に、テストします。そして、もう一度テストします。そして、リリース時にその状態が保持されることを確認してください。もしそうなら... まあ、あなたにはバグがあります。

これは、リリース コードでこのエラーを確認してから処理する必要があるということですか?

あなたがそれが起こると予想するなら、はい。状態をチェックし、例外をスローし、繊細に処理します。エラー レポートを送信します。ログ ファイルに書き込みます。ソフトウェアを更新します。

于 2012-08-21T21:53:59.323 に答える
1

C ライブラリが提供する assert に限定されるわけではありません。プロダクション/リリース ビルドでも動作する追加の assertion メカニズムを持つことは理にかなっています。

  • assert生産/リリース ビルドを遅くしたくない高価な (CPU、キャッシュ ホース、データベースの負荷など) チェックには C ライブラリを使用します。
  • 本番/リリースビルドでも実行したい安価なテストまたは非常に重要なテストには、独自のアサートメカニズムを使用します。失敗すると、プログラムがその後適切に動作することを信頼できないことが示されます。たとえば、プログラムのコアであるデータ構造が操作は明らかに破損しています
  • エラーを報告して、さらに作業を行うのに役立つ状態に戻ることができると思われる場合は、例外/エラー コードなどを使用し、そのサービスを提供し続けることが優先されます。

したがって、あなたの例では:

std::vector<int> v = get_stuff();
ASSERT(v.size() == this->size());
a = v[this->size() - 1];

デバッグ モードのみの ASSERT を使用することも、本番環境でも開始される ASSERT を使用することも、前者に続いて...

a = v.at(this->size() - 1);

...問題が本番環境で発生した場合にキャッチして処理できる例外が発生するようにします。例外処理ケースのコード カバレッジを取得するには、運用ビルド用の単体テスト ケースを作成する必要があります。

心に留めておくべきことは、現実的で保守可能なバランスを見つけることです。実行時のエラー処理を徹底的に行おうとすると、コードのサイズと複雑さが 5 倍または 10 倍になり、テストの労力も増大する可能性があります。そのため、どこでどの程度処理するかを選択してください。単純なアサートやコア ダンプなどは比較的単純です。テスト ケースのない 1 つのライナーであり、より自由に使用できます。

于 2012-08-21T23:29:24.373 に答える
1

アサーションは、常に真でなければならない条件用です。例外処理用ではありません。

これらは、設計中に作成した仮定を反映しています。が間違ったサイズのベクトルを返す可能性がある場合は、それを個別に処理する必要があります。get_stuff

于 2012-08-21T22:00:07.677 に答える
0

私の好みは、ASSERT (デバッグ用) の後にエラーを処理するコードを含めることです。デバッグの実行では、アサーションの失敗によりシステムが停止し、すぐに問題を確認できます。その後、リリース ビルドでは、エラー状況を処理するコードが引き続き実行されます。もちろん、エラーを処理するコードは考え抜かなければなりません。クラッシュ ダンプ ファイル (Windows ではミニダンプ、Linux ではコア ダンプ) を生成することは理にかなっています。

于 2012-08-21T21:54:45.407 に答える
0

通常、プログラムでは 3 種類のエラーに「対処」する必要があります。個人的には、次の方法 (ymmv) でエラーを処理および分類することを好みます。

  • 非常に頻繁に発生すると予想されるエラー (不適切なユーザー入力など)。それらは (部分的に) 回復可能である可能性があります。私は通常、チェック/エラーコードを介してそれらを処理しますが、例外を介して処理することもあります。
  • 検出されるが、通常は予期されない例外的なエラー。私は常に例外を使用します。通常、大規模な操作は失敗します。
  • 発生しない可能性があるエラー。それは「起こりえない」ことです。ベクター クラスの場合と同様に、人々は適切なインデックスを使用することを期待します。リリース モードでこれらの条件をチェックするために貴重なサイクルを費やしたくはありませんが、それらを検出するのが簡単な場合は、デバッグ モードで実行して、デバッグが困難なクラッシュのより良い代替手段を提供します (たとえば、assert(condition && "explanation of why you asser the condition here and what it means that it is violated").

私は通常、アサートを使用して関数の前提条件をテストします。たとえば、一部のポインターが null でないか、値が範囲内にあるかなどです。デバッグ モード (すべてのテストと単体テストが実行されている場所) でこれらのバグを見つけることができるように、それらを惜しみなく振りかけます。これは、かなりの標準ライブラリにある「未定義の動作を検出する」モードに少し似ています。

于 2012-08-21T22:04:46.467 に答える