引数と戻り値
関数間でデータをやり取りする方法。値渡しと参照渡し
概要
関数にデータを渡す方法と、 関数から結果を受け取る方法は、 C++の基本的かつ最も大切なスキルです。
この章では、値渡しと参照渡しの違いを理解し、 適切なデータ交換方法を選べるようになります。
値渡し(Value Passing)
値渡しはデフォルトの方法です。 関数に渡された値はコピーされ、 関数内での変更は元の変数に影響しません。
#include <iostream>
void increment(int x) {
x = x + 1; // x はコピーなので、元の値は変わらない
std::cout << "関数内: " << x << std::endl;
}
int main() {
int num = 10;
increment(num);
std::cout << "関数外: " << num << std::endl;
return 0;
}
関数内: 11
関数外: 10
num は 10 のままです。
これは関数が num のコピーである x を変更したからです。
参照渡し(Reference Passing)
参照渡しでは、 元の変数への「参照」を渡すため、 関数内での変更が元の変数に反映されます。
記号は & です。
#include <iostream>
void increment(int & x) { // & が参照を示す
x = x + 1;
std::cout << "関数内: " << x << std::endl;
}
int main() {
int num = 10;
increment(num);
std::cout << "関数外: " << num << std::endl;
return 0;
}
関数内: 11
関数外: 11
今度は num が 11 に変わりました。
関数が元の変数そのものを変更したからです。
値渡し vs 参照渡し
| 特性 | 値渡し | 参照渡し |
|---|---|---|
| 記号 | int x |
int & x |
| 元の値への影響 | なし(コピー) | あり(参照) |
| メモリ効率 | 悪い(コピー) | 良い(参照のみ) |
| 用途 | 入力値のみ使用 | 値を変更したい |
| リスク | 低い | 意図しない変更も可 |
複数の戻り値(参照渡しの活用)
通常、関数は 1つ戻り値しか返せません。 しかし参照渡しを使えば、複数の値を「戻す」ことができます。
例:整数を秒・分・時間に変換
#include <iostream>
void convertTime(int totalSeconds, int & hours, int & minutes, int & seconds) {
hours = totalSeconds / 3600;
minutes = (totalSeconds % 3600) / 60;
seconds = totalSeconds % 60;
}
int main() {
int h, m, s;
convertTime(3661, h, m, s); // 1時間1分1秒
std::cout << h << "時間 " << m << "分 " << s << "秒" << std::endl;
return 0;
}
1時間 1分 1秒
3つの参照を受け取ることで、 複数の結果を「返す」ことができます。
const参照(読取専用)
関数に値を渡すときに状況に応じて:
- 入力のみ(変更しない) → const参照
- 入力と出力(変更する) → 参照
- 入力のみ(小さいデータ) → 値渡し
例
#include <iostream>
#include <string>
// const 参照:読取専用、効率的
void printName(const std::string & name) {
std::cout << "名前: " << name << std::endl;
// name = "太郎"; // エラー! const だから変更できない
}
// 参照:値を変更する
void addOne(int & num) {
num = num + 1;
}
int main() {
std::string myName = "花子";
printName(myName); // 安全で効率的
int x = 5;
addOne(x);
std::cout << x << std::endl; // 6
return 0;
}
戻り値の活用
例1: 複数の計算結果
#include <iostream>
struct Point {
int x, y;
};
Point movePoint(Point p, int dx, int dy) {
Point result;
result.x = p.x + dx;
result.y = p.y + dy;
return result;
}
int main() {
Point p = {5, 10};
Point p2 = movePoint(p, 3, 4);
std::cout << p2.x << ", " << p2.y << std::endl; // 8, 14
return 0;
}
例2: 条件付き戻り値
#include <iostream>
int divide(int a, int b, bool & success) {
if (b == 0) {
success = false;
return 0;
}
success = true;
return a / b;
}
int main() {
bool ok;
int result = divide(10, 2, ok);
if (ok) {
std::cout << "結果: " << result << std::endl;
} else {
std::cout << "ゼロで除算できません" << std::endl;
}
return 0;
}
結果: 5
実践例
例:ソート関数
#include <iostream>
void swapValues(int & a, int & b) {
int temp = a;
a = b;
b = temp;
}
void bubbleSort(int arr[], int size) {
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
swapValues(arr[j], arr[j + 1]);
}
}
}
}
int main() {
int numbers[] = {5, 2, 8, 1, 9};
int size = 5;
bubbleSort(numbers, size);
for (int i = 0; i < size; i++) {
std::cout << numbers[i] << " ";
}
std::cout << std::endl; // 1 2 5 8 9
return 0;
}
1 2 5 8 9
ポイント
- 値渡し:コピーなので元の値は変わらない
- 参照渡し(&):元の値を直接変更できる
- const参照:読取専用で効率的
- 大きなデータは参照で渡す方が効率的
- 参照は複数の「戻り値」を返すのに有効
よくある誤り
誤り1: 参照渡しを忘れてしまう
void increment(int x) { // & がない!
x++;
}
int main() {
int num = 10;
increment(num);
std::cout << num << std::endl; // 10 のまま
}
誤り2: const 参照に値を代入しようとする
void process(const int & x) {
x = 10; // エラー! const だから変更不可
}
誤り3: 配列への誤解
void fillArray(int arr[10]) { // 配列は自動的に参照
arr[0] = 100; // 元の配列が変更される
}
やってみよう
練習1: 2つの変数の値をスワップ(交換)する関数。
練習2: 平均と標準偏差を計算し、参照で返す関数。
練習3: 文字列を受け取り、大文字に変換する関数(参照渡し使用)。
チャレンジ: ベクトルの大きさを計算する関数と、単位ベクトルを計算する関数。
まとめ
- 値渡しと参照渡しの違いを理解する
- 参照渡しで複数の値を「返す」ことができる
- const参照で読取専用かつ効率的なインターフェース
- 適切な方法を選ぶことが、バグの少ないコードにつながる