C++の参照渡し入門:& の意味と値渡しとの決定的な違い

C++の関数呼び出しにおいて、引数を渡す基本的な方法は「値渡し(Pass-by-Value)」です。しかし、値渡しには「呼び出し元の変数を変更できない」という限界があります。

この問題を解決し、関数が呼び出し元のデータを直接操作できるようにする仕組みが「参照渡し(Pass-by-Reference)」です。

目次

値渡しの限界

前回の記事で解説したように、「値渡し」では、関数の仮引数には実引数のコピーが渡されます。そのため、関数内で仮引数の値をいくら変更しても、呼び出し元のオリジナル変数には一切影響がありません。

例えば、「ユーザーのスコアを0にリセットする」関数を値渡しで作成すると、意図した通りに動作しません。

#include <iostream>

// (失敗例) 値渡しでスコアをリセットしようとする
void resetScore_PassByValue(int userScore) {
    // ここで変更しているのは「コピー」された userScore
    userScore = 0;
    std::cout << "  (関数内) スコアを " << userScore << " にリセットしました。\n";
}

int main() {
    int myScore = 1500;
    std::cout << "(呼び出し前) 現在のスコア: " << myScore << "\n";

    // myScore (1500) の「コピー」が関数に渡される
    resetScore_PassByValue(myScore);

    std::cout << "(呼び出し後) 現在のスコア: " << myScore << "\n";
    
    return 0;
}

実行結果:

(呼び出し前) 現在のスコア: 1500
  (関数内) スコアを 0 にリセットしました。
(呼び出し後) 現在のスコア: 1500

main 関数内の myScore1500 のまま変更されていません。

参照 (Reference) とは?

この問題を解決するのが「参照」です。

**参照(Reference)は、C++の機能で、すでに存在する変数(オブジェクト)に別名(エイリアス)**を付けるものです。参照は & 記号を使って宣言します。

#include <iostream>

int main() {
    int originalValue = 10;

    // originalValue への参照(別名)として refValue を宣言
    int& refValue = originalValue;

    std::cout << "オリジナル: " << originalValue << "\n"; // 10
    std::cout << "参照: " << refValue << "\n";         // 10

    // 参照(別名)の値を変更する
    refValue = 100;

    std::cout << "--- 変更後 ---\n";
    std::cout << "オリジナル: " << originalValue << "\n"; // 100 (オリジナルが変わる)
    std::cout << "参照: " << refValue << "\n";         // 100
    
    return 0;
}

refValueoriginalValue のコピーではなく、originalValue そのものを指す別の名前になります。そのため、refValue への操作は、originalValue への操作と全く同じになります。

参照渡し (Pass-by-Reference)

この「参照」の仕組みを関数の仮引数に応用したものが「参照渡し」です。

仮引数の型を int から int& に変更するだけで、その仮引数は「渡された変数のコピー」ではなく、「渡された変数そのものの別名(参照)」として機能します。

参照渡しによる swap 関数の修正

先ほどの resetScore 関数を、参照渡しを使って正しく動作するように修正します。

#include <iostream>

// (成功例) 参照渡しでスコアをリセットする
// 仮引数 userScore は、渡された変数の「参照」となる
void resetScore_PassByReference(int& userScore) {
    // ここでの変更は、呼び出し元の変数「そのもの」を変更する
    userScore = 0;
    std::cout << "  (関数内) スコアを " << userScore << " にリセットしました。\n";
}

int main() {
    int myScore = 1500;
    std::cout << "(呼び出し前) 現在のスコア: " << myScore << "\n";

    // myScore (1500) の「参照」が関数に渡される
    resetScore_PassByReference(myScore);

    std::cout << "(呼び出し後) 現在のスコア: " << myScore << "\n";
    
    return 0;
}

実行結果:

(呼び出し前) 現在のスコア: 1500
  (関数内) スコアを 0 にリセットしました。
(呼び出し後) 現在のスコア: 0

今度は main 関数内の myScore が正しく 0 に変更されました。

参照渡しの応用と注意点

参照渡しは、関数が複数の値を変更する必要がある場合に特に有効です。

応用例:2つの口座残高を調整する

以下の例では、transferFunds(資金移動)関数が、参照渡しを使って2つの異なる変数(accountAaccountB)の値を同時に操作しています。

#include <iostream>

// 口座Aから口座Bへ指定額を移動する
void transferFunds(int& fromAccount, int& toAccount, int amount) {
    // 参照を介して呼び出し元の変数を直接変更する
    fromAccount -= amount;
    toAccount += amount;
}

int main() {
    int accountA = 50000;
    int accountB = 10000;

    std::cout << "--- 移動前 ---\n";
    std::cout << "口座A: " << accountA << "円\n";
    std::cout << "口座B: " << accountB << "円\n";

    // 15000円を A から B へ移動
    transferFunds(accountA, accountB, 15000);

    std::cout << "--- 移動後 ---\n";
    std::cout << "口座A: " << accountA << "円\n"; // 35000
    std::cout << "口座B: " << accountB << "円\n"; // 25000

    return 0;
}

注意点:呼び出し側の見た目

参照渡しには重要な注意点があります。それは、関数を呼び出す側のコードの見た目が、値渡しと全く同じであることです。

int value = 100;
resetScore_PassByValue(value);      // 値渡し
resetScore_PassByReference(value);  // 参照渡し (見た目は同じ!)

呼び出し側(main関数など)のコードだけを見ても、その関数が value を変更する可能性がある(参照渡し)のか、絶対に変更しない(値渡し)のか、区別がつきません。

このため、関数を呼び出す際は、その関数の**宣言(シグネチャ)**を常に確認し、仮引数が int& のように & 付きになっていないか注意する必要があります。

まとめ:const 参照という現代的なテクニック

「参照渡し」は、呼び出し元の変数を変更できる強力な機能ですが、意図しない変更を引き起こす危険性も伴います。

  • 値を変更する必要がある場合:void func(int& x)
    • 呼び出し元の値を変更したい場合にのみ、参照渡しを使用します。
  • 値を変更しないが、コピーを避けたい場合:void func(const std::string& s)
    • std::stringstd::vector のような大きなオブジェクトを「値渡し」すると、コピーに時間がかかります。
    • このような場合、const(変更不可)を付けた「const 参照渡し」を使います。
    • これにより、コピーのコストを回避しつつ(高速)、関数内で呼び出し元の変数を誤って変更することも防ぐ(安全)という、両方のメリットを得ることができます。

現代のC++プログラミングでは、この const 参照渡しが非常に重要なテクニックとなっています。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次