関数ポインタ
関数のアドレスを保存。動的な関数呼び出し
概要
関数も、メモリのどこかに保存されており、 アドレスを持っています。
その関数のアドレスを指すのが関数ポインタです。 これにより、実行時に「どの関数を呼ぶか」を動的に決定できます。
関数ポインタの宣言
基本形式
// 戻り値 (*ポインタ名)(引数) = 関数のアドレス;
int (*funcPtr)(int, int) = add;
例:加算関数のポインタ
#include <iostream>
int add(int a, int b) {
return a + b;
}
int main() {
// 関数ポインタの宣言と初期化
int (*funcPtr)(int, int) = add;
// 関数ポインタを通じて関数を呼ぶ
int result = funcPtr(3, 5);
std::cout << "結果: " << result << std::endl;
return 0;
}
結果: 8
funcPtr(3, 5)はadd(3, 5)と同じです。
ただし、どの関数を呼ぶかが実行時に決まります。
複数の関数を指す
例:計算機アプリ
#include <iostream>
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int multiply(int a, int b) {
return a * b;
}
int main() {
int (*operation)(int, int);
char op;
std::cout << "演算子(+,-,*): ";
std::cin >> op;
// どの関数を指すかを動的に決定
if (op == '+') {
operation = add;
} else if (op == '-') {
operation = subtract;
} else if (op == '*') {
operation = multiply;
}
int a = 10, b = 5;
std::cout << "結果: " << operation(a, b) << std::endl;
return 0;
}
関数ポインタの配列
例:関数の配列
#include <iostream>
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int div(int a, int b) { return a / b; }
int main() {
// 関数ポインタの配列
int (*operations[])(int, int) = {add, sub, mul, div};
for (int i = 0; i < 4; i++) {
std::cout << "演算 " << i << ": " << operations[i](10, 3) << std::endl;
}
return 0;
}
演算 0: 13
演算 1: 7
演算 2: 30
演算 3: 3
関数ポインタをパラメータにする
例:高階関数
#include <iostream>
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
// 関数ポインタをパラメータに受ける
void performOperation(int x, int y, int (*op)(int, int)) {
std::cout << "結果: " << op(x, y) << std::endl;
}
int main() {
performOperation(5, 3, add); // 8
performOperation(5, 3, multiply); // 15
return 0;
}
結果: 8
結果: 15
typedef で短縮表記
関数ポインタの型定義が長いので、 typedefで短縮できます。
#include <iostream>
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
// 関数ポインタの型を定義
typedef int (*MathOp)(int, int);
void calculate(int x, int y, MathOp op) {
std::cout << "結果: " << op(x, y) << std::endl;
}
int main() {
calculate(10, 5, add);
calculate(10, 5, sub);
return 0;
}
結果: 15
結果: 5
実践例:ソートアルゴリズム
#include <iostream>
// 2つの要素を比較する関数の型
typedef int (*Comparator)(int, int);
// 昇順の比較
int ascend(int a, int b) {
return a > b;
}
// 降順の比較
int descend(int a, int b) {
return a < b;
}
// バブルソート(比較関数を指定)
void bubbleSort(int arr[], int size, Comparator compare) {
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - i - 1; j++) {
if (compare(arr[j], arr[j + 1])) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main() {
int numbers[] = {5, 2, 8, 1, 9};
int size = 5;
// 昇順にソート
bubbleSort(numbers, size, ascend);
std::cout << "昇順: ";
for (int x : numbers) std::cout << x << " ";
std::cout << std::endl;
// 降順にソート
bubbleSort(numbers, size, descend);
std::cout << "降順: ";
for (int x : numbers) std::cout << x << " ";
std::cout << std::endl;
return 0;
}
昇順: 1 2 5 8 9
降順: 9 8 5 2 1
Modern C++:std::function
C++11以降、std::functionを使って、 より柔軟に関数型を扱えます。
#include <iostream>
#include <functional>
int add(int a, int b) { return a + b; }
int main() {
// std::function を使用
std::function<int(int, int)> op = add;
std::cout << "結果: " << op(5, 3) << std::endl;
// ラムダ関数も指定可能
op = [](int x, int y) { return x * y; };
std::cout << "結果: " << op(5, 3) << std::endl;
return 0;
}
結果: 8
結果: 15
std::functionはより扱いやすく、
modern C++での推奨方法です。
ポイント
- 関数ポインタで関数のアドレスを保存
- 実行時にどの関数を呼ぶかを動的に決定
- typedefで型定義を簡潔に
- 関数ポインタの配列で複数関数を管理
- Modern C++ではstd::functionが推奨
よくある誤り
誤り1: 関数ポインタと関数呼び出しの混同
int add(int a, int b) { return a + b; }
int (*funcPtr)(int, int) = add; // OK
int (*funcPtr)(int, int) = add(); // エラー!: 関数を実行してしまう
誤り2: シグニチャが異なる関数を割り当て
int add(int a, int b) { return a + b; }
double divide(double a, double b) { return a / b; }
int (*funcPtr)(int, int) = add; // OK
int (*funcPtr)(int, int) = divide; // エラー!: シグニチャが異なる
誤り3: 宣言時に波括弧をつけ忘れ
int* funcPtr(int, int); // これは関数の宣言(ポインタではない)
int (*funcPtr)(int, int); // 妃れが関数ポインタ
やってみよう
練習1: 異なる計算関数をポインタで切り替える計算機。
練習2: 関数ポインタの配列でコールバック処理。
練習3: 比較関数をパラメータにしたソートプログラム。
チャレンジ: std::function を使ったイベントハンドラー。
まとめ
- 関数ポインタで関数を動的に呼び出す
- 高階関数の実装が可能
- typedefで簡潔に表記
- Modern C++ではstd::functionの使用を検討