【MFC】ASSERT(条件) でバグを早期発見する

【MFC】ASSERT(条件) でバグを早期発見する

ASSERT で止まった場所は、前提が崩れた場所です。ここで止まるなら、その少し前から値か状態がおかしくなっています。

この記事では、Debug Assertion Failed! ダイアログの見方と、Releaseで消える性質を前提に ASSERT をどう置くかを整理します。


目次

最初に見るポイント

  • ASSERTDebugビルドでだけ有効です。Releaseでは消えます。
  • ダイアログが出たら、「再試行 (Retry)」 で止めると、その行から原因を追えます。
  • ASSERT の中に、副作用のある処理を書いてはいけません

ASSERTが失敗するとどうなるか

たとえば、次のようなコードがあるとします。

void CMFCDebugSandboxDlg::OnBnClickedBtnAssert()
{
    int* pNullPointer = NULL;

    ASSERT(pNullPointer != NULL);

    *pNullPointer = 100;
}

pNullPointer は明らかに NULL です。ここで ASSERT が失敗し、Debug Assertion Failed! ダイアログが出ます。

Debug Assertion Failed! と表示された Visual C++ のエラーダイアログ

このダイアログで 「再試行 (Retry)」 を押すと、Visual Studio が ASSERT の行で停止します。ここから Call Stack やローカル変数を見れば、どの前提が崩れたかをその場で確認できます。


ASSERT に書く式

ASSERT に入れるのは、「ここでは必ず真のはずだ」 という前提です。処理そのものではなく、処理前後の整合を確認する式を書きます。

void CMyView::OnDraw(CDC* pDC)
{
    ASSERT(pDC != NULL);

    // 描画処理
}

void CPlayer::TakeDamage(int nDamage)
{
    ASSERT(nDamage >= 0);

    m_nHP -= nDamage;

    ASSERT(m_nHP >= 0);
}

このように、NULL でないこと、負の値が入らないこと、添字が範囲内であることなど、前提条件の確認に使います。


Releaseでは消える

ASSERT は Releaseビルドで評価されません。開発中だけ重い整合チェックを入れたいときに使います。

void CGameManager::Update()
{
    ASSERT(VerifyAllObjectsStateAreValid());

    // 通常の更新処理
}

この書き方なら、Debugでは整合を強く確認し、Releaseでは動作を変えずに本処理だけを走らせられます。


副作用を入れない

Releaseで消える以上、ASSERT の中に処理本体を書いてはいけません。ここを混ぜると、Debugでだけ動いて Releaseで壊れます。

// NG
ASSERT(m_file.Open(_T("data.txt"), CFile::modeRead) != FALSE);

この書き方だと、Releaseでは Open 自体が実行されません。結果として、Releaseでだけファイルを開かない不具合になります。

処理と確認は分けてください。

BOOL bOpened = m_file.Open(_T("data.txt"), CFile::modeRead);
ASSERT(bOpened != FALSE);

VERIFY を使う方法もありますが、まずは「処理は外、確認だけ ASSERT」に揃える方が混乱しません。


まとめ

  • ASSERT は、前提が崩れた場所で止めるための確認です。
  • Debugでダイアログが出たら、「再試行 (Retry)」 で止めて、その場から追ってください。
  • Releaseでは消えるため、副作用のある処理は中に書かないでください。
目次