質問

アサーションは、条件が満たされているかどうかを確認するために使用され(前提条件、事後、不変)、プログラマーがデバッグフェーズ中に穴を見つけるのを助けます。

例えば、

void f(int *p)
{
  assert(p);
  p->do();
}

私の質問は、リリースモードで条件を満たすことができないと仮定し、それに応じてケースを処理する必要があるかどうかです。

void f(int *p)
{
  assert(p);

  if (p)
  {
    p->do();
  }
}

結局のところ、アサーションとは、テストする条件が偽りではないことを意味します。しかし、もし私たちがそれをチェックせずに失敗した場合、プログラムはクラッシュします。ジレンマのように聞こえます。あなたたちはそれにどのように対処しますか?

役に立ちましたか?

解決

アサーションが失敗した場合、 プログラムはクラッシュするはずです.

アサーションの失敗とは、プログラマーがプログラムの流れがどのように進行するかを理解することに根本的な間違いを犯したことを意味します。これは開発援助であり、生産援助ではありません。生産では、処理する可能性があります 例外, 、「可能性がある」ため、アサーションは「失敗することはありません」。

あなたが「ああ、しかし、アサーションが生産に失敗したらどうなるか?私はそれらを捕まえる必要がある!」その後、ポイントがありません。そのような場合、自問してください、 なぜあなたは例外を投げかけていないのですか (または、エラーを処理する)?

一般的に言えば、 主張するいいえ 「条件が満たされていない場合、例外をスローする」の速記(まあ、それが運用上のセマンティクスであることもありますが、それは表現セマンティクスではありません)。むしろ、アサーションの失敗は、アプリケーションがにあることを意味します 開発者がそうであるとは信じていない状態 可能. 。そのような場合、コードを実行し続けることを本当に望んでいますか?明らかに(私が言うだろう)、 いいえ.

他のヒント

防御プログラミングが常に最適です。すべてのテストにもかかわらず、アプリケーションがバグで出荷されると常に想定する必要があります。そのため、ヌルのポインターの敬意を避けて先に進むことができる状況でヌルチェックを追加することはあなたの最大の利益です。

ただし、クラッシュを回避する簡単な方法が単にない状況があり、そのような場合、開発サイクル中に問題を検出する唯一の方法が主張されています。

ただし、1つの重要なポイント - アサートは、データの整合性に関する大きな問題を検出するためにもよく使用されます。これらのアサートを通過し続けると、データの破損が危険にさらされる可能性があります。そのような場合、データを破壊するよりもクラッシュする方が良いかもしれません。 (明らかに、少なくともエラーの説明を備えた合理的なUIをもたらすあらゆる種類のクラッシュハンドラーが望ましいでしょう)。

厳密に言えば、2番目のコードには冗長性があります。

void f(int *p)
{
  assert(p);
  if (p)    // Beats the purpose of assertion
  {
    p->do();
  }
}

アサーションは、エラーが発生したことを意味します。予期しない/扱われていない何か。上記のコードでも

1)Pがnullである場合に適切に処理しています。 (p-> do()を呼び出さないことによって) - これはおそらく正しい/期待されることです。ただし、アサーションはaです 誤警報.

2)一方、p-> do()を呼び出さないと、何かが問題になる場合(おそらくコードまたは出力でさらに)、アサーションは正しいが、とにかく継続することには意味がないはずです。

上記のコードでは、プログラマーはとにかく誤ったケースを処理するために一生懸命働いています。

とはいえ、一部の人々は主張をとして扱うのが好きです 何かが間違っていますが、それでも正しい出力が得られるかどうかを確認しましょう. 。 IMO、それは悪い戦略であり、バグ修正中に混乱を引き起こします。

アサーションは、コードを操作するのではなく、コードのデバッグです。それらを使用して入力エラーをキャッチしないでください。

アサーションは、テストでバグをキャッチするために使用されます。理論は、リリースしたら動作することを知るのに十分なほどテストしたということです。

実際の操作で状態が発生する可能性がある場合は、アサーションに依存しないでください - 例外または他のエラーメカニズムを使用してください。

あなたが言ったように、アサートはデバッグに役立ちます。彼らはそれを生産コードに決して巻き込んではいけません(コンパイルされているように、もちろん#ifdefsでそれらをラップしても大丈夫です)

あなたが是正することがあなたの制御を超えている問題に遭遇している場合、あなたがあなたの制作コードにチェックインする必要がある場合、私は次のようなことをします:

void f(int *p)
{

  if (!p)
  {
    do_error("FATAL, P is null.");
  }

  p->do();
}

ここで、do_errorはエラーを記録してきれいに終了する関数です。

リリースビルドに残したままにしておきます。どのビルドにもバグがあります。製品にアサートをすることは、問題をより簡単に特定することができることを意味します。

例外を処理することにあまり努力しないでください。 Stacktraceを含む完全な例外を手に入れることができることを確認してください。これは、特にàリリースビルドに適用されます。

多くの人がリリースモードにアサーションを入力することについてコメントしているので:

私が取り組んでいることで、効率は非常に重要です(時には、大規模なデータセットでの実行が完了するのに数十時間から数日かかります)。したがって、デバッグコードでのみ主張する特別なマクロがあります(QAなどで実行)。例として、forループ内のアサートは間違いなくオーバーヘッドであり、リリースコードでそれを回避することをお勧めします。結局のところ、すべてのIZがうまくいけば、アサートは失敗することは想定されていません。

リリースコードが良いと主張する1つの例は、ロジックが特定のコードのブランチをまったく押すことになっていない場合です。この場合、Assert(0)は問題ありません[したがって、あらゆる種類のアサート(0)は、常にリリースコードに残ることができます)。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top