1. ホーム
  2. c++

[解決済み] C++でassert()を使うのはバッドプラクティスか?

2022-11-21 22:33:52

質問

私は、リリース ビルドのパフォーマンスに影響を与えずにデバッグを容易にするために、自分の C++ コードに多くのアサーションを追加する傾向があります。現在 assert は、C++ のメカニズムを考慮せずに設計された純粋な C マクロです。

一方、C++では std::logic_error を定義しています。これはプログラムのロジックにエラーがある場合にスローされることを意図しています(それゆえにこの名前がついています)。インスタンスを投げることは、よりC++的で完璧な assert .

問題は assertabort はどちらもデストラクタを呼ばずにプログラムを即座に終了させるので、 クリーンアップを省略することができますが、手動で例外を投げることは不必要な実行コストを追加することになります。これを回避する一つの方法として、独自のアサーションマクロを作成し SAFE_ASSERT を作成することです。これは C の対応部分と同じように動作しますが、失敗したときに例外をスローします。

この問題に対して、3つの意見が考えられます。

  • Cのアサートにこだわる。 プログラムは即座に終了するので、変更が正しく展開されたかどうかは問題ではありません。また #define を C++ で使うのも同じように悪いことです。
  • main()で例外を投げ、キャッチする。 . プログラムのどのような状態でも、コードがデストラクタをスキップすることを許すのは悪い習慣で、何としても避けなければなりませんし、terminate()の呼び出しも同様です。例外がスローされた場合、それらはキャッチされなければなりません。
  • 例外をスローして、プログラムを終了させる。 例外でプログラムが終了するのは問題なく、原因として NDEBUG により、リリースビルドでは決して起こりません。キャッチは不要であり、内部コードの実装の詳細を main() .

この問題に対する決定的な答えはあるのでしょうか?何か専門的なリファレンスはありますか?

編集しました。 デストラクタをスキップすることは、もちろん未定義の動作ではありません。

どのように解決するのですか?

アサーションはC++のコードでは完全に適切です。例外やその他のエラー処理メカニズムは、アサーションと同じことを意図しているわけではありません。

エラー処理は、エラーを回復したり、ユーザにうまく報告したりする可能性があるときのためのものです。例えば、入力ファイルを読もうとしたときにエラーが発生した場合、それについて何かしたいかもしれません。エラーはバグから発生することもありますが、単に与えられた入力に対して適切な出力をすることもあります。

アサーションは、APIが通常チェックされないときにAPIの要件が満たされていることをチェックしたり、開発者が構築によって保証されていると信じていることをチェックしたりするためのものです。例えば、あるアルゴリズムがソートされた入力を必要とする場合、 通常はそれをチェックしません。しかし、デバッグビルドがその種のバグに気づくように、 それをチェックするためのアサーションがあるかもしれません。アサーションは常に、正しく動作していないプログラムを示すべきです。


もし、不正なシャットダウンが問題を引き起こす可能性のあるプログラムを書いているならば、アサーションを避けた方がよいかもしれません。なぜなら、アサーションに当たることは、おそらくすでに未定義の動作の結果であるか、正しく機能するためのクリーンアップを妨げる可能性のある他の要件に違反することだからです。

また、例外の観点からアサーションを実装した場合、アサーションの目的そのものに矛盾しているにもかかわらず、潜在的に捕捉され「処理」される可能性があります。