はじめに
C++では、NULL
は #define NULL 0
のように定義されており、本来使うべきではないところ(ポインターに関係のないところ)でも使用できてしまいます。
C++11ではNULL
の様々な問題点を解消するためにnullptr
キーワードが導入されました。nullptr
は整数型に暗黙変換できず、より安全です。今日ではNULL
は使うべきではありません。
なぜNULL
は良くないのか、過去に見た事例を振り返りながら復習してみましょう。
NULL文字とNULLの混同
NULL文字('\0'
)とNULLポインター(NULL
)は異なる概念のものですが、名前が似ているためか、混同している事例がありました。偶然にも値が一致しているので正しく動作します。
間違いの例:
char text[256];
text[0] = NULL;
修正例:
char text[256];
text[0] = '\0';
nullptr
は整数型へ暗黙変換できないため、このような問題は生じません。
char text[256];
text[0] = nullptr; // エラー
文字列オブジェクトの先頭をNULLと比較している
NULL文字とNULL
の混同である上に、無駄にわかりにくいコードです。
間違いの例:
const string str = ...;
if (*str.c_str() != NULL) { ... }
修正例:
const std::string str = ...;
if (!str.empty()) { ... }
0とNULLの混同
整数の0とNULL
は偶然にも値が一致しますが、異なる概念のものですから混同してはいけません。
間違いの例:
double d = NULL;
修正例:
double d = 0;
nullptr
は整数型へ暗黙変換できないため、このような問題は生じません。
double d = nullptr; // エラー
falseとNULLの混同
false
とNULL
は偶然にも値が一致しますが、異なる概念のものですから混同してはいけません。
間違いの例:
bool f() {
...
return NULL;
}
修正例:
bool f() {
...
return false;
}
nullptr
は整数型へ暗黙変換できないため、このような問題は生じません。
bool f() {
return nullptr; // エラー
}
コンテナをNULLで初期化している
整数0との混同ですが、異なる勘違いが背景にあるかもしれません。
間違いの例:
std::vector<int> v(NULL);
修正例:
std::vector<int> v;
nullptr
は整数型へ暗黙変換できないため、このような問題は生じません。
std::vector<int> v(nullptr); // エラー
コンストラクタはexplicit
にする
std::vector
では起こりませんが、このとき呼び出されるコンストラクタがexplicit
でない場合は、以下のようなコードも通ってしまいます。
MyArray arr = NULL;
暗黙変換を意図する場合を除き、1引数のコンストラクタは必ずexplicit
を指定しましょう。
class MyArray{
...
explicit MyArray(int){ ... }
};
MyArray a = 0; // エラー: explicitのため暗黙変換はできない
MyArray a(0); // OK
純粋仮想関数を宣言する0とNULLの混同
どんな教科書にもおそらく載っておらず、なぜこのようなコードが書かれたのかは謎です。
間違いの例:
virtual void f() = NULL;
修正例:
virtual void f() = 0;
もちろん、nullptr
であれば文法エラーとなります。
まとめ
nullptr
を使いましょう。
また、こうしたミスを発見するには、NULL
をnullptr
に置換することが効果的です。