C++言語講座 第2章 1回 参照
さあ、いよいよ実践編、第2章です。ついて来れていますか? これから2回程、直接は応用しづらい内容が続きますので、具体的な例が少なくなるかも知れませんが、ちゃんとついて来て下さいね。
参照
参照
昔説明したように、「代入」はあくまでオブジェクトの値をコピーすることを指します。つまり、下の [List 1] のように、
a = x;
とした後に、
x = 20;
としても、変わるのはxの値だけで、aの値は最初のxの値のまま、という事です。
[List 1]
1 #include <iostream>
2 using namespace std;
3
4 int main()
5 {
6 int x = 10;
7 int a = x;
8
9 x = 20;
10
11 cout << x << endl;
12 cout << a << endl;
13
14 return 0;
15 }
これを何とかできないでしょうか? 「xを変えるとaも変わり、aを変えるとxもかわる」…といったことは出来ないでしょうか? [List 1]を以下のように変更してみてください。
[List 2]
1 #include <iostream>
2 using namespace std;
3
4 int main()
5 {
6 int x = 10;
7 int& a = x;
8
9 x = 20;
10
11 cout << x << endl;
12 cout << a << endl;
13
14 return 0;
15 }
変更したのはaの宣言だけです。しかし、これで、
x = 20;
と書くだけで、aの値も20に変わってしまったようです。今度は、
x = 20;
を
a = 30;
に変えてみましょう。この場合でも、aとxの値両方が変わっています。
ここでは、aを
int& a
と宣言しました。これは、aが通常のオブジェクトではなく、参照 (reference)であることを示しています。
参照がどのような働きをするのか見てみましょう。参照を宣言するときは、通常の型名の後に & を付けます。参照は宣言するときに、必ず初期化しなければなりません。
int& a = x;
これで、aはxの参照になりました。また、この状態をaがxを指しているという場合もあります。
cout << a << endl;
と書いた場合、(aではなく)xの内容が表示され、
a = 30;
と書いた場合、(aではなく)xに30が代入されます。このように、参照は他のオブジェクトへの仲介をしていると考えることが出来ます。
参照の引数
昔言った通り、引数もコピーでしか無いので、[List 3]でfunc関数の中で
a = 20;
としても、main関数のxの値は変わりません。
[List 3]
1 #include <iostream>
2 using namespace std;
3
4 void func(int a)
5 {
6 a = 20;
7 }
8
9 int main()
10 {
11 int x = 10;
12
13 func(x);
14
15 cout << x << endl;
16
17 return 0;
18 }
では、func関数の中で、main関数から渡された引数の値を変えたいときは、どうすれば良いでしょう。やはり、参照を使えば良いのです。[List 3]を以下のように書き変えて下さい。
[List 4]
1 #include <iostream>
2 using namespace std;
3
4 void func(int& a)
5 {
6 a = 20;
7 }
8
9 int main()
10 {
11 int x = 10;
12
13 func(x);
14
15 cout << x << endl;
16
17 return 0;
18 }
この場合でも、
a = 20;
という行で、main関数のxに20が代入されていると考えて下さい。
参照を使うときの注意
以下のプログラムは、多くの場合コンパイル中にエラーが出るか、実行中にバグります。何故でしょう?
[List 5]
1 #include <iostream>
2 using namespace std;
3
4 int& func()
5 {
6 int x = 10;
7
8 return x;
9 }
10
11 int main()
12 {
13 int& y = func();
14
15 cout << y << endl;
16
17 return 0;
18 }
関数funcの返値を見て下さい。int&と参照になっていますね。以前で説明したように、関数の中で宣言したオブジェクトは、その関数の中でだけ有効です。つまり、オブジェクトxの有効範囲(スコープ)は関数funcの中だけなのです。このため、xはreturn文によりfuncの実行が終了するとすぐに破棄され、メモリから消されてしまいます。
funcはxの参照を返そうとしていますが、参照は常に何かを指していなければなりません。xはすぐに消えてしまうので、参照することは出来ないのです。
複数の参照の同時宣言
複数のオブジェクトを同時に宣言するときは、以下のように書きました。
int x, y, z;
同じように、複数の参照を同時に宣言してみましょう。
int& a, b, c;
しかし、こうして宣言しても、思い通りの処理にはなりません。実はこの場合、 a は参照になりますが、b と c は参照ではない普通のオブジェクトになってしまいます。何故でしょう?
int& a, b, c;
と書くと、int の後の & は a とペアになっているように解釈されてしまうのです。つまり、
int& a; int b; int c;
と書いているようなものなのです。これを阻止するためには
int &a, &b, &c;
と書く方法が有るのですが、あまり綺麗では有りません。ですから、参照を宣言する場合は、一気に宣言せずに、1つひとつ宣言するようにしましょう。
int& a; int& b; int& c;
といった感じですね。
メンバ関数
参照先のオブジェクトのメンバ関数を実行する事も出来ます。通常のオブジェクトと同じように、
1 string str;
2 string& a = str;
3
4 a.length();
とすると、aの参照しているオブジェクト(つまり、str)のメンバ関数lengthが実行されます。
参照の型名
参照は、普通のオブジェクトと違うところもありますが、やはりオブジェクトの一種であることには変わりありません。ということは、参照にも型があるという事です。例えば、
1 int x;
2 int& a = x;
というように宣言された参照 a の型名は何でしょうか? 言っておきますが、 int型ではありません。この a の型名はあくまでint&型です。参照を宣言するときにつける & は参照の型名の一部であることを、頭の片隅に留めておいて下さい。
参照の問題点
今、参照aはオブジェクトxを指しているとします。つまり、以下のように宣言されたわけです。
int x;
int& a = x;
では、aの指している先を変えることは出来るでしょうか? つまり、aを
int y;
と宣言されたyの参照に変えることが出来るでしょうか?
a = y;
としても、xにyの値が代入されるだけです。結論からいえば、これは不可能です。参照が指している先は、宣言するときにしか決めることが出来ないのです。だからこそ、参照は必ず初期化する必要があるわけです。
では、ここで書いたように、指している先をどうしても変えたいときは、どうすれば良いでしょう? ここで、次回説明する「ポインタ」が役に立ってきます。乞う御期待。
