参照: ポインタより安全な「あだ名」

C++の重要な機能。NULLにならず、再束縛もできない安全なポインタとして理解する。

参照 (Reference)
既存の変数に対する別名(あだ名)。実体は同じ。
別名 (Alias)
参照の役割そのもの。
初期化 (Initialization)
変数を宣言すると同時に値を設定すること。参照は宣言時の初期化が必須。
const参照
const修飾子が付いた参照。参照先を読み取ることしかできない。

詳細解説

あだ名 (Nickname)

参照は変数につける「あだ名」です。「鈴木さん」という人がいて、友達が「スーさん」と呼ぶようなものです。「スーさん」に何かを渡せば、それは「鈴木さん」に渡したことになります。実体は一人しかいません。

なぜ参照が必要か?

参照は主に2つの目的で使われます。

  • 安全性: 参照は宣言時に必ず初期化が必要で、NULLになることがありません。これにより、ポインタで頻発するNULLポインタアクセスエラーをコンパイル時点で防げます。
  • 可読性: 関数に参照を渡すと、呼び出し側のコードがすっきりします。値渡しのように見えながら、実際には参照として機能するため、コードの意図が明確になります。
参照の基本
// int 型の変数 original を宣言し、10 で初期化
int original = 10;
// original への参照 alias を作成
// これ以降、alias は original と全く同じものを指す
int& alias = original;
// 参照(あだ名)を使って値を変更
alias = 20;
// original(本名)の値も変わっている
// 出力: original: 20, alias: 20
std::cout << "original: " << original << ", alias: " << alias << std::endl;
// 参照は NULL にできず、必ず初期化が必要
// int& no_init; // コンパイルエラー
// int& null_ref = nullptr; // コンパイルエラー
実行結果
original: 20, alias: 20

ポインタ vs 参照 徹底比較

どちらも変数のメモリアドレスを扱う点で似ていますが、重要な違いがあります。

特性 ポインタ (`*`) 参照 (`&`)
NULLにできるか はい (<code>nullptr</code>) いいえ
初期化 宣言時に必須ではない 宣言時に必須
再束縛(指す相手の変更) 可能 不可能
値へのアクセス デリファレンス (`*`) が必要 不要(変数と同様に扱う)
主な用途 動的メモリ確保、配列、指す相手が変わる可能性 関数引数・戻り値、エイリアス
関数引数での比較
// ポインタ版: アドレスを渡す必要があり、呼び出し側が冗長
void swap_ptr(int* a, int* b) {
if (a == nullptr || b == nullptr) return; // NULLチェックが必須
int temp = *a;
*a = *b;
*b = temp;
}
// 参照版: 呼び出しが自然で、NULLの心配がない
void swap_ref(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int x = 5, y = 10;
swap_ptr(&x, &y); // アドレスを渡す
swap_ref(x, y); // 値を渡すように見える

実践テクニック

<code>const</code>参照による効率的な関数渡し

関数に大きなオブジェクト(クラスや構造体)を渡すとき、値渡しをするとオブジェクト全体のコピーが発生し、パフォーマンスが低下します。これを避けるため、「const参照」を使うのがC++の定石です。これにより、コピーを防ぎつつ、関数内でオブジェクトが誤って変更されることも防止できます。

巨大なオブジェクトを安全かつ効率的に渡す
// 巨大なオブジェクトを想定
struct BigObject {
// ... たくさんのデータ ...
std::string data;
};
// ❌ Bad: 値渡し。BigObject全体のコピーが発生し、非効率。
void process_by_value(BigObject obj) {
std::cout << obj.data << std::endl;
}
// ✅ Good: const参照渡し。コピーが発生せず、読み取り専用で安全。
void process_by_const_ref(const BigObject& obj) {
// obj.data = "new value"; // コンパイルエラー!変更できない
std::cout << obj.data << std::endl;
}

演習課題

課題1: 参照渡し
引数で受け取った`int`型の変数の値を1増やす`increment`という関数を、参照を使って実装してください。
解答例を見る
void increment(int& value) {
value++;
}
int main() {
int counter = 10;
increment(counter);
// counter は 11 になる
return 0;
}
課題2: ポインタから参照へ
ポインタを受け取ってその値を表示する関数を、参照を受け取るようにリファクタリングしてください。呼び出し側のコードはどう変わりますか?

合格ライン

参照が変数の「あだ名」であることを自分の言葉で説明できる
ポインタと参照の主な違いを3つ挙げられる(NULL, 再束縛, 初期化)
関数に大きなオブジェクトを渡す際に`const`参照がなぜ良いかを説明できる

参考リンク

C++ reference - cppreference.com - (公式に近い信頼性の高いリファレンス)
C++ Super-FAQ - References - (よくある質問とその答え)