ポインタと参照の使い分け その2


以前、どこかで読んだ、ポインタ/参照使い分けの、間違ったガイドライン


函数の仮引数を定義するとき、函数内で変更される引数はポインタ、変更されない引数は参照にする。

念のために書いておくと、ポインタや参照渡しは、オブジェクトのコピーが発生しない分、コストの削減になる。だから、函数内部で実引数が変更されない場合でも、オブジェクトがでかいときや、テンプレートなどで型が決められないときなど、よく使用される。だから、変更されない引数を参照で渡すこと自体は、問題ではない。
このガイドラインの根拠は、コードを読むにあたって、函数に渡す実引数がポインタであれば変更されそう、オブジェクトであれば変更されなさそう、という目星が付くということだった。実際、身近にも、実引数にオブジェクトが与えられていると、「これは変更されないのだ」という認識を持つ人はいた。
これは、三つの点から間違っている。


第一に、呼ばれる函数の仕様は確認しなければならない。「目星」でコードの機能を判定してはならない。
第二に、ポインタや参照であることと、それが函数内部で変更されるか否かはまったく関連性がない。constを用いれば、値の変更は禁止できる。
第三に、コードの意味が変わる。ポインタと参照には、前回示したような意味がある。参照を使うべきところにポインタが使われていると、ポイント先が無効であったり、変更されたりする前提でコードを読まねばならない。文法(シンタックス)に誤りが無くても、意味(セマンティクス)がおかしいコードになってしまう。


以下のような仕様の函数があるとする。



void stringLength( const std::string * pText, unsigned & length )
{
// pText はポインタなので、NULL判定が必要。
if( 0 == pText )
{
length = 0; // length は参照なので、NULL判定は不要。
}
else
{
length = pText->size();
}
}

この函数を呼ぶコードは、以下の通り。



void hoge()
{
std::string * pText = 0;
unsigned length = 0;

stringLength( pText, length );
}

ポインタを渡しても値は変更されないし、オブジェクトを渡しているように見えても値が変更される。
また、ポインタはNULL値が渡される可能性があるので、その判定をしなければならない。


コードは、その機能自体が意味を表す。逆に言えば、持たせたい意味に相当する機能のコードを書かなければならない。
コードを書くことは、コメントに匹敵するほど詳細に語るということであり、そのコードに一致しない意味を持たせることはできない。
また従って、コメントはコードの機能以前に、まず意味を書くべきだといえる。