C++の関数は、return 文を使って呼び出し元に値を返すことができますが、返却できる値は原則として1つだけです。
もし、1つの関数で「複数の計算結果」を呼び出し元に返したい場合、どうすればよいでしょうか。例えば、一連のデータを分析して「最大値」と「最小値」の両方を取得したい場合などです。
この問題を解決する伝統的な(C言語由来の)方法が、ポインタ(アドレス)を関数に渡すテクニックです。
ポインタ渡しとは?
「ポインタ渡し」とは、変数の値そのものを渡す(値渡し)代わりに、変数が格納されているメモリアドレス(& 演算子で取得)を関数に渡す方法です。
関数側(仮引数)は、そのアドレスを**ポインタ(*)**として受け取ります。
これにより、関数は受け取ったポインタを間接参照(* 演算子)することで、呼び出し元の関数のスコープにあるオリジナルの変数を直接読み書きできるようになります。
ポインタ渡しのサンプルコード
以下の例では、findMinMax という関数が、商品の価格リスト(double型配列)を分析し、その結果(minPrice と maxPrice)を呼び出し元の main 関数が用意した変数に書き込みます。
#include <iostream>
#include <array> // std::array を使用
#include <limits> // 最小値・最大値の初期化に使用
/**
* @brief 配列から最小値と最大値を見つけ、ポインタ経由で結果を書き込む
* @param data (in) 分析対象のデータ
* @param outMin (out) 最小値を格納する場所へのポインタ
* @param outMax (out) 最大値を格納する場所へのポインタ
*/
void findMinMax(const std::array<double, 5>& data, double* outMin, double* outMax)
{
// ポインタが nullptr でないかチェック (安全のため)
if (outMin == nullptr || outMax == nullptr) {
return;
}
double currentMin = std::numeric_limits<double>::max();
double currentMax = std::numeric_limits<double>::lowest();
for (double price : data) {
if (price < currentMin) {
currentMin = price;
}
if (price > currentMax) {
currentMax = price;
}
}
// --- ここが重要 ---
// ポインタが指す先のメモリアドレスに、見つけた値を書き込む
*outMin = currentMin;
*outMax = currentMax;
}
int main() {
std::array<double, 5> productPrices = {19.99, 25.50, 15.00, 30.10, 18.75};
// 1. 結果を格納するための「箱」(変数)を main 関数で用意する
double minResult = 0.0;
double maxResult = 0.0;
// 2. findMinMax 関数に、変数の「アドレス(&)」を渡す
findMinMax(productPrices, &minResult, &maxResult);
// 3. 関数が main の変数を変更したことを確認する
std::cout << "--- 商品価格分析結果 ---\n";
std::cout << "最小価格: " << minResult << " ドル\n";
std::cout << "最大価格: " << maxResult << " ドル\n";
return 0;
}
実行結果:
--- 商品価格分析結果 ---
最小価格: 15.00 ドル
最大価格: 30.10 ドル
main 関数は minResult と maxResult のアドレス(&)を渡しました。findMinMax 関数は、それらを outMin と outMax というポインタとして受け取り、*outMin = ... と *outMax = ... という操作で main 関数側の変数を直接変更しました。
現代C++における代替手段
この「ポインタ渡し」による出力方法はC++で有効なテクニックですが、いくつかの欠点があります。
- 呼び出し側は
&を付ける必要があり、面倒。 - 関数側は
*で間接参照する必要があり、読みにくい。 nullptrを渡される(あるいは渡してしまう)危険性がある。
現代のC++では、同じ目的を達成するために、より安全でクリーンな代替手段が推奨されます。
代替案1:参照渡し (&)
前章で解説した「参照渡し」は、ポインタ渡しの最も直接的な代替手段です。
// ポインタ (double*) の代わりに 参照 (double&) を使用
void findMinMax_Ref(const std::array<double, 5>& data, double& outMin, double& outMax)
{
// ... (計算ロジックは同じ) ...
// '*' や '->' が不要になり、通常の変数と同じように代入できる
outMin = currentMin;
outMax = currentMax;
}
int main() {
double minResult = 0.0;
double maxResult = 0.0;
// 呼び出し側で '&' が不要になり、クリーンになる
findMinMax_Ref(productPrices, minResult, maxResult);
// ...
}
C++において、このような「出力用引数」には、nullptr を渡せない(安全性が高い)という理由から、ポインタ渡しよりも参照渡しが圧倒的に好まれます。
代替案2:struct や std::tuple を返す
最も現代的で推奨される方法は、出力用引数を一切使わず、複数の値をまとめた struct(構造体)や std::tuple を return で返すことです。
#include <iostream>
#include <array>
#include <limits>
#include <tuple> // std::tuple や std::tie を使用
// 戻り値の型として、2つの double を持つ std::tuple を指定
std::tuple<double, double> findMinMax_Return(const std::array<double, 5>& data)
{
// ... (計算ロジックは同じ) ...
// 最小値と最大値をタプルとして返す
return std::make_tuple(currentMin, currentMax);
}
int main() {
std::array<double, 5> productPrices = {19.99, 25.50, 15.00, 30.10, 18.75};
double minResult = 0.0;
double maxResult = 0.0;
// 戻り値を変数に分解して受け取る (C++11 の std::tie)
std::tie(minResult, maxResult) = findMinMax_Return(productPrices);
// (C++17以降では、以下のようにさらに簡潔に書けます)
// auto [minResult, maxResult] = findMinMax_Return(productPrices);
std::cout << "最小価格: " << minResult << " ドル\n";
std::cout << "最大価格: " < < maxResult << " ドル\n";
return 0;
}
この方法は、関数が「何を入力(引数)とし、何を(return)で出力するのか」が明確になるため、最も読みやすく安全なコードとされています。
まとめ(総括)
- **ポインタ渡し(
Type*)**は、関数の引数として変数のアドレス(&)を受け取り、間接参照(*)によって呼び出し元の変数を変更する伝統的なテクニックです。 - これにより、関数から複数の値を「返す」ことが(擬似的に)可能になります。
- 現代のC++では、同じ目的のために、より安全で可読性の高い**参照渡し(
Type&)**が推奨されます。 - さらに言えば、出力用引数を使わずに、
structやstd::tupleで複数の戻り値をカプセル化し、returnで返す方法が最も好ましい設計とされています。
