第44章 デバッグ文

 C/C++の標準ライブラリには、第17章のデバッグ文を利用したマクロが用意されています。今回はそのお話です。


 それでは、今回の要点です。


 では、いってみましょう。


 いきなりですが、次のプログラムを見て下さい。

プログラム
// Assert.cpp
#include <iostream.h>

// 動的配列クラス
class CArray
{
private:
    int* m_pArray;  // 動的配列
    int  m_nSize;   // 配列のサイズ

public:
    enum Exception  // 例外型
    {
        IrregalSize,   // サイズ指定異常
        BadAlloc,      // メモリ確保失敗
        IrregalIndex,  // 不正なインデックス
        NotAlloc,      // メモリを確保していない
    };

public:
    // コンストラクタ
    CArray(int nSize);
    virtual ~CArray(){ Release(); }

    // 要素の取得
    int& operator[](int index)
    {
        if(m_pArray == NULL)
            throw NotAlloc;
        if(index < 0 || m_nSize <= index)
            throw IrregalIndex;
        return m_pArray[index];
    }
    // サイズの取得
    int GetSize(){ return m_nSize; }
    // メモリの解放
    void Release();
};

// コンストラクタ
CArray::CArray(int nSize)
    : m_pArray(NULL), m_nSize(0)
{
    if(nSize <= 0)
        throw IrregalSize;
    m_pArray = new int[nSize];
    if(m_pArray == NULL)
        throw BadAlloc;
    m_nSize = nSize;
}

// メモリの解放
void CArray::Release()
{
    if(m_pArray != NULL)
    {
        delete [] m_pArray;
        m_pArray = NULL;
        m_nSize  = 0;
    }
}

int main()
{
    CArray array(10);
    int    i;

    for(i = 0; i < array.GetSize(); i++)
        array[i] = i;
    for(i = 0; i < array.GetSize(); i++)
        cout << array[i] << ' ';
    cout << endl;

    return 0;
}

 簡単な動的配列クラスを作って、それを実際に使っています。コピーコンストラクタ、= 演算子オーバーライド、再確保などができないあたりかなり手抜きですが、完全な動的配列クラスを作るのが目的ではないので許して下さい。

 では、先ずはコンストラクタを見てみましょう。

// コンストラクタ
CArray::CArray(int nSize)
    : m_pArray(NULL), m_nSize(0)
{
    if(nSize <= 0)
        throw IrregalSize;
    m_pArray = new int[nSize];
    if(m_pArray == NULL)
        throw BadAlloc;
    m_nSize = nSize;
}

 先ずメンバ変数を初期化して、引数のチェックを行っています。nSize が不正な場合は IrregalSize 例外を返すようにしています。

 次に、メモリの確保をしています。確保できなかった場合は BadAlloc 例外を返すようにしています。そして、確保ができたら m_nSize に nSize を代入しています。

 あと目立った関数といえば、[ ] 演算子くらいでしょう。

// 要素の取得
int& operator[](int index)
{
    if(m_pArray == NULL)
        throw NotAlloc;
    if(index < 0 || m_nSize <= index)
        throw IrregalIndex;
    return m_pArray[index];
}

 要素を取得するわけですが、その前にメンバ変数をいちいちチェックしています。こういったチェック機能を簡単に付けられるのはクラスの嬉しいところですが、ちょっと冗長な感じもしますね。なにしろ、ちゃんとした完成品のプログラムではこういったチェックには引っかからないようにしてあることが望まれるからです。

 こういうときには #ifdef を駆使して制御するのでしたね。しかし、値の判定については自分で作らなくても既に便利なマクロが用意されています。それが assert です。

#include <assert.h>  // assert を使うのに必要

// 要素の取得
int& operator[](int index)
{
    assert(m_pArray != NULL);
    assert(0 <= index && index < m_nSize);
    return m_pArray[index];
}

 assert 文は、その引数がのときに abort します。つまり、引数には常に成り立っていて欲しい条件式を書くことになります。

 そして、assert 文は次のようにすれば効力を失います。

#define NDEBUG
#include <assert.h>

// 要素の取得
int& operator[](int index)
{
    assert(m_pArray != NULL);
    assert(0 <= index && index < m_nSize);
    return m_pArray[index];
}

 NDEBUG を assert.h をインクルードする前に定義してやれば、assert 文は空の処理に置き換わります。これで assert 文は書いていないのと同じ事になり、無駄な処理は完全に消えて無くなります。

 VC++では、プロジェクトの設定をリリースモード、つまり、Win32 Release にすると(第18章参照)、自動的に NDEBUG が定義されます。これも _DEBUG と同じところで定義してあります。

 assert.h の中身を見ると自分でデバッグ文を作るときの参考になるかも知れませんね。VC++や gcc での定義を見てみましたが、なかなかに面白い定義の仕方で参考になります。解説したいところですが、コードの引用に問題があってはいけないので自粛しておきます。その謎は自分で解き明かしてみて下さい。


 今回の要点は以下の通りです。


 しばらくマニアックな話題が続くかも知れませんが、ご容赦下さい。それでは。


第43章 切り捨て御免 | 第45章 マクロの落穂拾い

Last update was done on 2001.5.5

この講座の著作権はロベールが保有しています