C++ NULLポインタ経由のメンバ関数呼び出し
実際に試してみると死なない
実際に試したところでは特に死んだりはしないようです
struct cfoo { void foo() const { printf( "foo\n" ); } }; static_cast<cfoo*>(0)->foo();
またこんな感じのも死なないようです
struct cfoo { cfoo* find( const char* name ) const { if( !this ) return NULL; cfoo* result = NULL; children_t::const_iterator p = m_children.find( name ); if( p ){ result = p->second; } return result; } cfoo* parent() const { if( !this ) return NULL; return m_parent; } }; cfoo* q = p->find( "aaa" )->parent();
C的には当たり前
C的に考えれば、非仮想関数で非staticなメンバ関数は第一引数が this であるだけの普通の関数なので、第一引数に NULL を渡すか有効なアドレスを渡すかで動作が変わるわけはありません
呼び出した先で渡したアドレスをデリファレンスしないのであれば死なないのは当たり前です
しかし未定義動作
しかし C++ の標準としては未定義動作です
- static_cast
(0)->foo() と (*static_cast (0)).foo() は同義である - *static_cast
(0) は明らかに NULLポインタをデリファレンスしている - NULLポインタのデリファレンスは未定義動作と定められている
- 従って、NULLポインタ経由のメンバ関数呼び出しは未定義動作である
呼び出し先で this をデリファレンスしなければ OK的な記述は見つかりませんでした
ちゃんと NULL チェックをしましょう
this の NULL チェックをしましょう。じゃないよ
製品となるプログラミングで重要なのは標準ではなく製品です
標準に従ったコードなんだから製品に問題があってもそれは処理系が悪いんだ。などという言い訳が通るはずはないのですから、最終的には標準よりも実際に出力されたコードの挙動の方が重要なのは当たり前です
しかしだからと言って死なないんだから何をしてもいいんだ。標準なんて関係ない。ということでもありません
死なないかどうかに関係なくポインタは使う前にちゃんと NULL チェックをしましょう
余談
こちらは静的な問題で、関数呼び出しは動的な問題ですが
これも C++ では未定義な動作です
const char *p = 0; const char *q = &*p;
しかしこれは未定義動作ではありません
char a[10] = {}; const char *p = &a[10]; // &a[10-1] + 1 としなくてもよい ptrdiff_t celt = p - a;
(上は NULLアクセスの問題。下はオーバーフローの問題)
参考:
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232