閑古鳥

オールドプログラマの日記。プログラミングとか病気(透析)の話とか。

参照にしたからといって安全になるわけでもない

C++ の参照は必ず初期化が必要となる (必ず実体がある) ので、参照を取る引数はポインタを取る引数を違って引数が NULL かどうかを判断する必要がありません。

……というようなことは色々なところで述べられています。それを理由にアドレスを引数に取る関数は、ポインタではなく参照で宣言することを勧めている所も多いのですけれど、しかし、参照にしたからといって安全になるわけではなかったりします。

#include <iostream>
using namespace std;

void f(const int& n)
{
    cout << n << endl;
}

int main()
{
    int* p = 0;
    f(*p);
    
    return 0;
}

非常に嫌らしい例ですけれども、このコードは実行時にエラーとなります。 f() が受け取ったアドレスが無効なためです。

まあ、こんな嫌がらせみたいなコードは普通書かないでしょうし、そんなことを心配するだけ無駄というものですが、「この引数は参照だから絶対安全」と盲信してしまうと NULL 参照が原因のバグが出た時に見逃す可能性もあるかもしれませんという事で。

ちなみにこの例はポインタを受け取った時と同様、アドレスが NULL かどうか調べれば一応エラーを防ぐことはできます。 assert(&n != 0) とか。でもこれだとポインタと変わりありませんし、まあ、参照を引数に取る関数はその時点で「NULL は受け付けないよ」という表明になっていると判断して呼び出し側はちゃんとした値を渡すようにするべきでしょう (何回か意図せぬ NULL 参照を召喚してしまったので自戒を込めつつ……)。

Joel 本 (asin:4274066304) で NULL 参照によるエラーが出たけど有り得ないバグだから無視したよ、とあるけれど、これ有り得るよなー、と書いている最中に思いました。

ちなみにこれを書くきっかけになったのは Consider Using References Instead of Pointers - C++ Optimization Strategies and Techniques という記事。こちらでは NULL 参照は一応作れるとは書いてありますね。