2006年01月31日

関数とリファレンス

関数には引数と戻り値が存在し、それぞれに変数, ポインタ, リファレンスを用いることができます。また、関数へのポインタと同様に、関数へのリファレンスというものも存在します。本章では、このような関数とリファレンスの関係について考えていこうと思います。

001 void f1(int i)
002 {
003 i += 10; // 単なるコピーに10を加える。
004 }
005
006 void f2(int* p)
007 {
008 *p += 10; // ポインタの参照先に10を加える。
009 }
010
011 void f() {
012 int i = 10;
013
014 f1(i);
015 cout << i << endl; // i の値は変化しない。(10を出力)
016 f2(&i);
017 cout << i << endl; // i の値は変化する。(20を出力)
018 }

しかし、C++ にはリファレンスによる「参照渡し」が存在します。仮引数にリファレンスを指定すれば、関数呼び出しによって引数を変更することができます。

001 void f3(int& i)
002 {
003 i += 10; // i は与えられた変数の別名。
004 }
005
006 void f() {
007 int i = 10;
008
009 f3(i);
010 cout << i << endl; // i の値は変化する。(20を出力)
011 }

つまり、C++ には関数呼び出しによって引数を変更する手段が2通り存在することになります。ポインタとリファレンスで述べたようにポインタには何も参照しない状態(ヌルポインタ)が存在するため、ポインタを用いて引数を変更する場合にはヌルポインタに気をつける必要があります。それでは、ポインタとリファレンスの使い分けは、関数呼び出しによって引数を変更する場合でも「何も参照しない場合があるかどうか」で区別すればよいのでしょうか。これに関しては「プログラミング言語 C++ 第3版」の「5.5 リファレンス」にて以下のように述べられています。

しかし、プログラムを読みやすくするために、引数を変更する関数は避けた方がよい場合が多い。代わりに、明示的に値を返すか、ポインタ引数を要求するのである。
- 中略 -
increment(x) という表記は、x=next(x)、incr(&x) とは異なり、xの値が変更されようとしていることを示す手掛かりを読者に与えない。そのため、“ただの”リファレンス引数は、リファレンス引数が変更されるというヒントをはっきりと与えるような名前が付けられた関数だけで使うようにすべきである。

先ほどの例で言えば、「f1(i) と同じような感覚で f3(i) を呼び出して i の値が変化してしまうと読みづらいプログラムになってしまう」といったところでしょうか。f2(&i) であれば、i の値が変更されるかも知れないという事を意識して以降の処理を読み進めることができます。

ところで、C言語の時代では、引数を変更する以外にもポインタ引数を用いる場合がありました。それは、コピーに時間がかかる場合です。先ほども述べたように「値渡し」では引数がまるごとコピーされてしまいます。これでは巨大な構造体を引数として渡したい場合に無駄なコピーが大量に発生してしまうため、C言語ではポインタを渡してこの問題に対処していました。このままでは引数を変更することもできますが、もし、引数を変更する必要がない場合には const なポインタを渡すようにするだけです。

そして、同じ事がリファレンスにも当てはまります。C++ では、無駄なコピーを抑えるためにリファレンスを用いることができ、値を変更したくない(させない)場合には const なリファレンスを使用します。この const なリファレンス引数は、C++ のプログラムにおいて最も頻繁に登場するリファレンスといえるでしょう。

引数の「参照渡し」には他にも以下のような利点があります。

* 不要なコンストラクタの呼び出しを抑える。
* スライシングを防止する。
* コピーコンストラクタが public でなくても使える。

詳しくは「Effective C++ 改訂2版」の「22項 値渡しよりも、リファレンス渡しを使おう」を参照してください。
posted by シンビアン at 09:02| Comment(0) | TrackBack(0) | Symbian OS C++ 実践開発技法 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。