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

Flat Leon Works

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

【C++ 小ネタ】ポインタ変数の初期化はif文の条件式で行うと一石三鳥

関数の戻り値やdynamic_cast結果のポインタはNULLの可能性があります。そしてNULLポインタへのアクセスはプログラムをクラッシュさせたりします。

Hoge* p_hoge = GetHoge();
p_hoge->SetValue( 4 ); // p_hogeがNULLだとアウト

当然、if文でNULLチェックを行えばNULLポインタへのアクセスを回避できます。

Hoge* p_hoge = GetHoge();
if ( p_hoge )
{
    p_hoge->SetValue( 4 ); // p_hogeはNULLチェック済み
}

しかしもっと良い方法があります。

if ( Hoge* p_hoge = GetHoge() )
{
    p_hoge->SetValue( 4 ); // p_hogeはNULLチェック済み
}

実はこのように、if文の条件式では変数の宣言が許されています。このように記述することでポインタ変数の初期化NULLチェックを同時にできます。さらにこの方法にはもう一つメリットがあります。ポインタ変数のスコープがif文内に限定されることです。「【C++ 小ネタ】ブロック文を活用する」に書いたように変数の利用範囲を狭めることは、コードの可読性を向上させます。

ただし、すべてのポインタ変数の初期化をif文で行えばよいというわけではありません。ポインタ変数の利用が局所的でない場合や初期化するポインタ変数が複数個ある場合などは普通に初期化したほうがいいでしょう。

ちなみに、if文だけでなくwhile文やfor文の条件式でも使えるようです。switch文やdo-while文では使えません。

#include <stdio.h>

int valueList[] = { 3, 0, 1 };
int* GetPtr(int i){ return i < 3 ? valueList + i : 0; }

int main() {
    {
        int i = 0;
        while( int* p = GetPtr(i) )
        {
            printf( "%d\n", *p ); // 出力 3 0 1
            ++i;
        }
    }
    for ( int i=0; int* p = GetPtr(i); ++i )
    {
        printf( "%d\n", *p ); // 出力 3 0 1
    }
    /* コンパイルエラー
   switch( int* p = GetPtr(0) )
   {
       case 0:break;
       default:break;
   }
   */
    /* コンパイルエラー
   {
       int i = 0;
       do
       {
           ++i;
       } while( int* p = GetPtr(i) );
   }
   */
    return 0;
}

まとめ

ポインタ変数の初期化をif文条件式で行うと一石三鳥

  • ポインタ変数の初期化
  • NULLチェック
  • ポインタ変数の利用範囲を狭められる