C++を知りたい(3) 参照
C++には、参照という機能がある。ポインタと似たことができる機能らしいけど、いまいち使いどころがわからない。
int a; int& rA = a; //変数aに参照rAを結び付ける
「参照というのは、宣言した時に変数を結びつけて、後から変数の結びつけを変更することができない」という説明を聞くと、関数型言語の変数のバインドを思い出すんだけど、C++だとそれが嬉しいことがあるのかが良くわからない。
関数の例を見ながら、もっと簡単にしたバージョンで確認してみる。
#include <iostream> using namespace std; void pointer(int* x) { *x = 100; } void reference(int& x) { x = 100; } int main() { int a = 1; cout << "= pointer =" << endl; cout << "before: " << a << endl; pointer(&a); cout << "after : " << a << endl; a = 1; cout << "= reference =" << endl; cout << "before: " << a << endl; reference(a); cout << "after : " << a << endl; return 0; }
$ g++ ref.c $ ./a.out = pointer = before: 1 after : 100 = reference = before: 1 after : 100
引数でポインタを受けて呼び出し元の値を変更するpointerという関数と、引数で参照を受けて呼び出し元の値を変更するreferenceという関数を定義した。結果が同じなので、これを元に考えてみる。
pointer
main関数では、aという変数はint型なので、pointer関数に渡すにはアドレス値を渡す必要があるので、&を付けてアドレス値をpointerの引数に指定した。pointer関数の内部では、xに*を前置して、アドレス値が指しているint型の値を更新している。
reference
main関数では、aという変数はint型だが、reference関数の参照の引数にそのまま指定できている。参照をそのまま更新している。
最初の「参照はint&で宣言しますよ」の説明の後、いきなり関数呼び出しの例が出てくるのは、なんか説明を省きすぎているように感じた。関数の引数呼び出し時に、呼び出し元の変数に仮引数の参照がバインドされるという話なんだろうか。
引数がintの場合は、説明はしやすいけど、利点もわかりにくい。クラスのインスタンスが引数になった場合のことは、「やさしいC++」のクラスの基本の箇所に書いてあった。
Carというクラスがあった場合、Carクラスを引数にする場合
void buy(Car c) { int n = c.getNum(); ... }
buyの引数がCar型ということは、関数呼び出し時にスタックにインスタンスのコピーが丸ごと積まれるということ。関数呼び出しのオーバーヘッドが大きい。
Carというクラスがあった場合、Carクラスのポインタを引数にする場合
void buy(Car* c) { int n = c->getNum(); ... }
buyの引数がCarのポインタということは、関数呼び出し時にスタックにポインタだけが積まれる。関数呼び出しのオーバーヘッドが小さい。だけど、Carクラスのメソッドの呼び出し方をポインタ式に変更する必要がある。
Carというクラスがあった場合、Carクラスの参照を引数にする場合
void buy(Car& c) { int n = c.getNum(); ... }
buyの引数がCarの参照ということは、関数呼び出し時にスタックに参照だけが積まれる。関数呼び出しのオーバーヘッドが小さい。
参照の利点
Carを引数にした関数呼び出しのオーバーヘッドを減らしながら、メンバに対するアクセス方法も変更しなくていいようにするには、参照を引数にすればいいということか。だとすると、参照で解決したかった問題って、他のほとんどの後発言語がスマートに解決した問題ということだなー。