リリースビルドのパフォーマンスに影響を与えずにデバッグを容易にするために、C++コードに多くのアサーションを追加する傾向があります。現在、assert
は、C++メカニズムを考慮せずに設計された純粋なCマクロです。
一方、C++は_std::logic_error
_を定義します。これは、プログラムのロジックにエラーがある場合(名前の由来)にスローされることを意図しています。インスタンスをスローすることは、assert
に代わる、完全なC++風の代替案にすぎないかもしれません。
問題は、assert
とabort
の両方がデストラクタを呼び出さずにプログラムを即座に終了するため、クリーンアップをスキップし、例外を手動でスローすると不必要なランタイムコストが追加されることです。これを回避する1つの方法は、独自のアサーションマクロ_SAFE_ASSERT
_を作成することです。これは、Cの対応マクロと同様に機能しますが、失敗すると例外をスローします。
この問題に関する3つの意見を考えることができます:
#define
_ sを使用することも同様に悪いことです。NDEBUG
が原因で、これは起こりません。リリースビルドで。キャッチは不要で、内部コードの実装の詳細をmain()
に公開します。この問題に対する決定的な答えはありますか?専門的な参考資料はありますか?
編集:デストラクタをスキップすることはもちろん、未定義の動作ではありません。
アサーションは、C++コードでは完全に適切です。例外やその他のエラー処理メカニズムは、実際にはアサーションと同じものを対象とするものではありません。
エラー処理は、エラーを回復したりユーザーに適切に報告したりする可能性がある場合に使用します。たとえば、入力ファイルを読み込もうとしてエラーが発生した場合、それについて何かしたいと思うかもしれません。エラーはバグに起因する可能性がありますが、特定の入力に対する適切な出力である可能性もあります。
アサーションは、APIが通常チェックされない場合にAPIの要件が満たされていることを確認すること、または開発者が構築によって保証されていると信じていることを確認することなどのためのものです。たとえば、アルゴリズムがソートされた入力を必要とする場合、通常はチェックしませんが、デバッグビルドがそのようなバグにフラグを立てるようにチェックするアサーションがある場合があります。アサーションは、常に正しく動作していないプログラムを示す必要があります。
クリーンシャットダウンが問題を引き起こす可能性があるプログラムを作成している場合は、アサーションを回避することができます。アサーションのヒットはおそらく未定義の動作の結果であるか、またはクリーンアップが適切に動作しなくなる可能性のある他の要件の違反であるため、C++言語に関して厳密に定義されていない動作はここではそのような問題とは見なされません。
また、例外の観点からアサーションを実装すると、アサーションの本来の目的と矛盾する場合でも、キャッチされて「処理」される可能性があります。
アサーションは、デバッグ用です。出荷されたコードのユーザーは決してそれらを見るべきではありません。アサーションがヒットした場合、コードを修正する必要があります。
例外は例外的な状況です。遭遇した場合、ユーザーは自分が望むことをすることはできませんが、別の場所から再開することができます。
エラー処理は、通常のプログラムフロー用です。たとえば、ユーザーに数字の入力を求めて解析不能な何かを取得した場合、それはnormalになります。ユーザーの入力はあなたの制御下になく、当然すべての可能な状況を常に処理する必要があるからです。 (たとえば、有効な入力が得られるまでループし、「申し訳ありませんが、もう一度やり直してください」と言ってください。)
すべてのabort()によるデストラクタの実行は未定義の動作ではありません!
もしそうなら、それはstd::terminate()
を呼び出すことも未定義の振る舞いであり、それを提供する意味は何でしょうか?
assert()
は、C++と同様にC++でも有用です。アサーションはエラー処理のためではなく、プログラムを即座に中止するためのものです。
アサーションは、メソッドの実行前後の内部状態など、内部実装の不変条件を検証するために使用できます。アサーションが失敗した場合、実際にはプログラムのロジックが壊れており、これから回復できません。この場合、ユーザーに例外を渡さずにできるだけ早くブレークすることが最善です。 (少なくともLinuxの場合)アサーションについて本当に素晴らしいのは、プロセスの終了の結果としてコアダンプが生成されるため、スタックトレースと変数を簡単に調査できることです。これは、例外メッセージよりもロジック障害を理解するのにはるかに役立ちます。
私見、アサーションは、違反した場合、他のすべてをナンセンスにする条件をチェックするためのものです。したがって、それらから回復することはできません。むしろ、回復は無関係です。
それらを2つのカテゴリにグループ化します。
float potential(){return -1.0; }
assert(probability()> 0.0)
int x = 1;
assert(x> 0);
これらはどちらも些細な例ですが、現実からそれほど遠くはありません。たとえば、ベクトルで使用するための負のインデックスを返す単純なアルゴリズムについて考えてください。または、カスタムハードウェアに埋め込まれたプログラム。むしろたわごとが起こるのため。
そして、そのような開発の間違いがある場合、実装された回復またはエラー処理メカニズムについて自信を持ってはいけません。同じことがハードウェアエラーにも当てはまります。