【C++】参照がわからなかったので整理した【超初心者向け】

# 背景
C++の学習を始めて、参照とかポインタとか、引数の値渡しとか参照渡しとかいまいちよくわからなかったので、特に参照について整理してみました。

# 前提
参照と、その元になる変数などを想定され得るパターンで初期化、再代入などを行ってみて、それぞれがどのようなふるまいになるかを確認してみました。

一部正式な用語ではない、本記事のみで使用する用語があります。

- ```int a = 10```における```10```を本記事では総称して**値リテラル**と呼んでいます。

- ```int a;```のような変数の宣言、や宣言のあとに利用される```a```を総称して**値型**と呼んでいます。

- ```int& a;```のような変数の宣言、や宣言のあとに利用される```a```を総称して**参照型**と呼んでいます。

- パターン表で```左辺```、```右辺```という言い方を使ってますが、```右辺値参照```とかでの意味合いではなく単に```A=B```という変数の初期化や代入式のAを```左辺```、Bを```右辺```と呼んでいるだけです。

# わかったことをざっくり整理
厳密な仕様はさておき、いったん私の頭で整理してみた内容をさっくりまとめると以下のようになります。

## そもそも変数とその参照とはなにか
- 変数の宣言とは、メモリ上のアドレスの確保である。変数はアドレスに対するラベルのようなもの。
- ひとつのアドレスに対して、複数の変数を指定することができる。
- 参照とは、元の変数に対して複数指定できる変数のことである。つまり、同一アドレスの別名である。

## 参照の振る舞い
- 参照などもったいぶった名前がついてはいるが、参照が元の変数の別名（同一アドレスの別名）でしか無いなら、元の変数と振る舞いは何ら変わらない（変数定義の仕方は&がついたりで異なるが、その後の振る舞いは元の変数と同じ）
- ただし、同じアドレスに対して複数の別名が存在するということは、ある一つの別名に対する操作がその他すべての別名に影響がある、ということ。引数の参照渡しとはこの原理を利用する。

## 変数の再代入
- 変数を再代入しても、それが通常の変数であろうが参照であろうが、初期化時のアドレスから更新されることはない。値のみ置き換えられる。

## 引数の値渡しとは
- 関数の引数の初期化を下記パターン表の&#9312;か&#9314;で行うこと。すなわち、関数内の引数が関数に渡された変数とは異なるアドレスに値がコピーされる。

## 引数の参照渡しとは
- 関数の引数の初期化を下記パターン表の&#9320;か&#9322;で行うこと。すなわち、関数内の引数が関数に渡された変数と同じアドレスの値を参照する。

# 確認のパターン表
<table>
	<tr>
		<th style="text-align:center" colspan="3"></th>
		<th style="text-align:center" colspan="4">右辺</th>
	</tr>
	<tr>
		<th style="text-align:center" colspan="3"></th>
		<th>値リテラル</th>
		<th>アドレス演算子</th>
		<th>値型</th>
		<th>参照型</th>
	</tr>
	<tr>
		<th style="text-align:center" rowspan="4">左辺</th>
		<th style="text-align:center" rowspan="2">値型</th>
		<th>初期化</th>
		<td>&#9312;　新規アドレス確保、値代入</td>
		<td>&#9313;　コンパイルエラー</td>
		<td>&#9314;　新規アドレス確保、値同じ←すなわちコピー</td>
		<td>&#9315;　新規アドレス確保、値同じ←すなわちコピー</td>
	</tr>
	<tr>
		<th>再代入</th>
		<td>&#9316;アドレス同じ、値更新</td>
		<td>&#9317;コンパイルエラー</td>
		<td>&#9318;アドレス同じ、値更新</td>
		<td>&#9319;アドレス同じ、値更新</td>
	</tr>
	<tr>
		<th style="text-align:center" rowspan="2">参照型</th>
		<th>初期化</th>
		<td>&#9320; 新規アドレス確保、値代入</td>
		<td>&#9321; コンパイルエラー</td>
		<td>&#9322; 値型のアドレスで初期化、値型の値</td>
		<td>&#9323; 参照型 のアドレスで初期化、参照型の値</td>
	</tr>
	<tr>
		<th>再代入</th>
		<td>&#9324; アドレス同じ、値更新</td>
		<td>&#9325; コンパイルエラー</td>
		<td>&#9326; アドレス同じ、値更新</td>
		<td>&#9327; アドレス同じ、値更新</td>
	</tr>
</table>

# 挙動の確認

## &#9312;　値型を値リテラルで初期化
```C++
int a = 10; // 値型を値リテラルで初期化
std::cout << "a = " << a << " &a = " << &a << std::endl;
```

```:出力結果
a = 10 &a = 0x7ffe7909889c
```

## &#9313;　値型を値リテラルで再代入
```C++
int a = 10; 
std::cout << "a = " << a << " &a = " << &a << std::endl;
a = 20; // 値型を値リテラルで再代入
std::cout << "a = " << a << " &a = " << &a << std::endl;
```

```:出力結果
a = 10 &a = 0x7ffcc922655c
a = 20 &a = 0x7ffcc922655c
```
初期化時の```a```のアドレスは同じまま、値が更新される。

## &#9314;　値型をアドレス演算子で初期化
```C++
int a = 10; 
int b = &a; // 値型をアドレス演算子で初期化→コンパイルエラー
```
アドレス演算子はポインタ型(int*)なので値型(int)に代入しようとしてエラーとなる。

## &#9315;　値型をアドレス演算子で再代入
```C++
int a = 10;
int b = 20;
b = &a;    // 値型をアドレス演算子で再代入→コンパイルエラー
```
&#9314;と同じ

## &#9316;　値型を値型で初期化
```C++
int a = 10;
std::cout << "a = " << a << " &a = " << &a << std::endl;
int b = a; // 値型を値型で初期化
std::cout << "b = " << b << " &b = " << &b << std::endl;
```

```:出力結果
a = 10 &a = 0x7ffebf12bb2c
b = 10 &b = 0x7ffebf12bb28
```
値型変数```b```に新しいアドレスを割り当てて、```a```と同じ値を設定する。

## &#9317;　値型を値型で再代入
```C++
int a = 10;
std::cout << "a = " << a << " &a = " << &a << std::endl;
int b = 20;
std::cout << "b = " << b << " &b = " << &b << std::endl;
a = b; // 値型を値型で再代入
std::cout << "a = " << a << " &a = " << &a << std::endl;
```

```:出力結果
a = 10 &a = 0x7ffc4c8ac5ac
b = 20 &b = 0x7ffc4c8ac5a8
a = 20 &a = 0x7ffc4c8ac5ac
```
値型変数```a```のアドレスはそのまま、値型変数```b```の値で更新される。

## &#9318;　値型を参照型で初期化
```C++
int a = 10;
std::cout << "a = " << a << " &a = " << &a << std::endl;
int& b = a;
std::cout << "b = " << b << " &b = " << &b << std::endl;
int c = b; // 値型を参照型で初期化
std::cout << "c = " << c << " &c = " << &c << std::endl;
```

```:出力結果
a = 10 &a = 0x7ffcccb45ff4
b = 10 &b = 0x7ffcccb45ff4
c = 10 &c = 0x7ffcccb45ff0
```
値型の変数```c```に新しいアドレスを割り当てて、参照型の変数```b```の値（値型```a```と同じ値）を設定する。

## &#9319;　値型を参照型で再代入
```C++
int a = 10;
int& b = a;
std::cout << "b = " << b << " &b = " << &b << std::endl;
int c = 20;
std::cout << "c = " << c << " &c = " << &c << std::endl;
c = b; // 値型を参照型で再代入
std::cout << "c = " << c << " &c = " << &c << std::endl;
```

```:出力結果
b = 10 &b = 0x7fff695cf714
c = 20 &c = 0x7fff695cf710
c = 10 &c = 0x7fff695cf710
```
値型の変数```c```のアドレスはそのまま、参照型の変数```b```の値で更新される。

## &#9320;　参照型を値リテラルで初期化
```C++
int& a = 10; // 参照型を値リテラルで初期化→コンパイルエラー
```
とおもいきや・・・

```C++
const int& a = 10; // 参照型を値リテラルで初期化
std::cout << "a = " << a << " &a = " << &a << std::endl;
```

```:出力結果
a = 10 &a = 0x7ffeef7e1244
```
この挙動はわからない・・・

## &#9321;　参照型を値リテラルで再代入
```C++
int a = 10;
int& b = a;
std::cout << "a = " << a << " &a = " << &a << std::endl;
std::cout << "b = " << b << " &b = " << &b << std::endl;
b = 20; // 参照型を値リテラルで再代入
std::cout << "a = " << a << " &a = " << &a << std::endl;
std::cout << "b = " << b << " &b = " << &b << std::endl;
```

```:出力結果
a = 10 &a = 0x7fff8547d2a4
b = 10 &b = 0x7fff8547d2a4
a = 20 &a = 0x7fff8547d2a4
b = 20 &b = 0x7fff8547d2a4
```
参照型変数```b```初期化時のアドレスはそのまま、値リテラルの値で更新される。
なお、```b```は```a```の参照なので、ｂを更新した場合はaの値も更新される。
一度```a```の参照として```b```のアドレスが割り当て済なので、値リテラルで値の更新が可能なのは理解できる。

## &#9322;　参照型をアドレス演算子で初期化
```C++
int a = 10;
int& b = &a; // 参照型をアドレス演算子で初期化→コンパイルエラー
```
intの参照型は実体はintなのでポインタ型(int***)ではないためエラーとなる。

## &#9323;　参照型をアドレス演算子で再代入
```C++
int a = 10;
int b = 20;
int& c = a;
c = &b; // 参照型をアドレス演算子で再代入→コンパイルエラー
```
&#9322;と同じ。

## &#9324;　参照型を値型で初期化
```C++
int a = 10;
int& b = a; // 参照型を値型で初期化
std::cout << "a = " << a << " &a = " << &a << std::endl;
std::cout << "b = " << b << " &b = " << &b << std::endl;
```

```:出力結果
a = 10 &a = 0x7ffe4d52f094
b = 10 &b = 0x7ffe4d52f094
```
いわゆるもっともシンプルな参照型の例。
参照型の変数```b```は値型の変数```a```と同じアドレスで、値も同じとなる。
つまり、参照型```b```とは```a```の別名である。

## &#9325;　参照型を値型で再代入
```C++
int a = 10;
int& b = a; // 参照型を値型で初期化
std::cout << "b = " << b << " &b = " << &b << std::endl;
int c = 20;
std::cout << "c = " << c << " &c = " << &c << std::endl;
b = c; // 参照型を値型で再代入
std::cout << "b = " << b << " &b = " << &b << std::endl;
```

```:出力結果
b = 10 &b = 0x7fff0a50cbe4
c = 20 &c = 0x7fff0a50cbe0
b = 20 &b = 0x7fff0a50cbe4
```
参照型```b```のアドレスはそのまま、値型```c```の値で更新される。
```b```のアドレスが```c```のアドレスで更新されるわけではない。

※参照への再代入という言い方は適切でないという[コメント](https://qiita.com/yachinco/items/8e1a764eb3118c6a3ad2#comment-70d96dd6ae94dfb017bb)をいただきました。便宜上```再代入```という用語を使用しています。

## &#9326;　参照型を参照型で初期化
```C++
int a = 10;
int& b = a;
std::cout << "a = " << a << " &a = " << &a << std::endl;
std::cout << "b = " << b << " &b = " << &b << std::endl;
int& c = b; // 参照型を参照型で初期化
std::cout << "c = " << c << " &c = " << &c << std::endl;
```

```:出力結果
a = 10 &a = 0x7ffefeedc98c
b = 10 &b = 0x7ffefeedc98c
c = 10 &c = 0x7ffefeedc98c
```
参照型の変数```c```のアドレスは参照型の変数```b```のアドレスで初期化され、すなわち値も```b```と同じとなる（参照型```b```の実体は値型```a```と同じなので、とうぜん```a```の値とも同じ）。

## &#9327;　参照型を参照型で再代入
```C++
int a = 10;
int& b = a;
int& c = b;
std::cout << "c = " << c << " &c = " << &c << std::endl;
int d = 20;
int& e = d;
std::cout << "e = " << e << " &e = " << &e << std::endl;
c = e; // 参照型を参照型で再代入
std::cout << "c = " << c << " &c = " << &c << std::endl;
```

```:出力結果
c = 10 &c = 0x7fff2a76acd4
e = 20 &e = 0x7fff2a76acd0
c = 20 &c = 0x7fff2a76acd4
```
参照型の変数```c```のアドレスはそのまま、値は参照型の変数```e```の値で更新される。
```c```のアドレスが```e```のアドレスで更新されるわけではない。

# まとめ
参照について整理しようとしていろいろ動作確認してたら発散してしまいましたが、よく聞く

- 参照とは別名（エイリアス）である

というところがいろいろ動かしてみたおかげでその意味するところが体感として理解することができました。

# さらに調べてみたいこと
- ではポインタとは？（TODO）
- 値渡しと参照渡しのふるまいの詳細を確認。以下で整理済。
 - [【C++】引数の値渡しと参照渡しを動かしながら理解【超初心者向け】](https://qiita.com/yachinco/items/ec5a5e3e751d6a490921)