読者です 読者をやめる 読者になる 読者になる

Flat Leon Works

アプリやゲームを作ってます。

【C++ 小ネタ】delete演算子とfree関数はNULLチェックは不要

delete演算子とfree関数はNULLチェックは不要

delete演算子とfree関数は、NULLを渡した場合、何も処理が行われないことが保証されています。なので以下のようなコードは、

if ( p )
{
    delete p;
}

このようにシンプルに書くことができます。

delete p;

malloc関数に対応するfree関数も同様です。

2重解放には注意が必要

delete演算子、free関数にNULLチェックは不要ですが、2重解放には注意が必要です。

class A
{
    int* m_pInt;
public:
    A() : m_pInt( 0 ){}
    void CreateInt( void )
    {
        DeleteInt();
        m_pInt = new int;
    }
    void DeleteInt( void )
    {
        delete m_pInt;
    }
};

int main() {
    A a;
    a.CreateInt();
    a.DeleteInt();
    a.CreateInt(); // <- ここで2重開放が発生
    return 0;
}

intを作成して、削除して、また作成しているプログラムですが、よくみると2重開放が発生しています。

プログラムの流れ

  • a.CreateInt();
    • DeleteInt();
      • delete m_pInt; // m_pIntはNULLなので何も起こらない
    • m_pInt = new int;
  • a.DeleteInt();
    • delete m_pInt; // m_pInt 解放
  • a.CreateInt();
    • DeleteInt();
      • delete m_pInt; // m_pInt また解放!?これは2重解放だ
    • m_pInt = new int;

何がいけなかったのかというと、delete m_pInt;したあとにm_pIntにNULLを入れて置かなかったことです。このような事態を避けるために、delete後のポインタには必ずNULLを代入するようにします。

以下のようにマクロやtemplateで「deleteとNULLポインタのセット」をまとめた処理を作ってしまうのもいいかもしれません。

// マクロバージョン
#define SAFE_DELETE( p ) delete p; p = 0

// templateバージョン
template<class T>
void SafeDelete( T*& p )
{
    delete p;
    p = 0;
}

int main()
{
    {
        int* p_int = new int;
        SAFE_DELETE( p_int );
    }
    {
        int* p_int = new int;
        SafeDelete( p_int );
    }
    return 0;
}