関数ポインタは、
C言語、
C++、
D言語などのプログラミング言語におけるポインタの一種で、関数(
サブルーチン)のアドレスを保持します。このポインタを介して関数を呼び出すことが可能です。関数ポインタは、switch文の代替となるテーブルジャンプの実装や、コールバック関数による処理のカスタマイズといった応用が可能です。
関数オブジェクトは、関数ポインタと似ていますが、関数ポインタがコード領域中のエントリポイントを指すのに対し、関数オブジェクトはデータ領域に実体を持つオブジェクトです。これにより、関数オブジェクトはデータを保持でき、
クロージャのような動作を実現できます。そのため、関数オブジェクトは、単なる関数ポインタよりも強力な機能を提供します。
C#やVisual Basic .NETなど、
.NET Framework用の言語では、メソッドを参照する型としてデリゲートがあります。P/Invokeなどの.NET相互運用においては、デリゲートは関数ポインタに変換されます。
Javaはバージョン8でメソッド参照を導入しましたが、バージョン7までは、メソッドを一つだけ持つインタフェースを利用して、同様の機能を実現する必要がありました。
第一級関数を持つ言語では、関数を引数として渡したり、戻り値として返したり、動的に生成したりできるため、関数ポインタは必ずしも必要ではありません。
Cでの関数ポインタの例
c
include
void my_function(int arg) {
printf("my_function called with arg: %d
", arg);
}
int main() {
void (func_ptr)(int); // 関数ポインタの宣言
func_ptr = my_function; // 関数アドレスの代入
func_ptr(10); // 関数ポインタ経由で関数呼び出し
return 0;
}
上記の例では、`func_ptr` が関数ポインタとして宣言され、`my_function`のアドレスが代入されています。その後、`func_ptr` を介して関数が呼び出されています。
補足:
関数シンボル自体は関数型であり、関数ポインタとは異なります。`&f`のようにアドレス演算子を付けることで、関数ポインタが得られますが、関数型の式は暗黙的に関数ポインタに変換されるため、アドレス演算子を省略できます。
関数ポインタ`fp`に間接演算子``を付けると関数指示子となりますが、
C言語では`fp(arg)`のように直接呼び出すことも可能です。
関数ポインタの宣言では、仮引数の名前は省略できます。
関数ポインタを引数として渡す例:
c
include
void my_function(int arg) {
printf("my_function called with arg: %d
", arg);
}
void caller(void (func_ptr)(int), int arg) {
func_ptr(arg); // 関数ポインタ経由で関数呼び出し
}
int main() {
caller(my_function, 20); // my_functionをcallerに渡す
return 0;
}
`caller`関数は、関数ポインタと整数値を引数として受け取り、関数ポインタを通して関数を呼び出します。これにより、`caller`はどんな関数でも引数として受け取ることができます。
積分計算の例:
c
include
double f(double x) {
return x x;
}
double integ(double (func)(double), double a, double b) {
double result = 0.0;
double step = 0.0001; // 精度
for (double i = a; i < b; i += step) {
result += func(i) step;
}
return result;
}
int main() {
double a = 0.0;
double b = 1.0;
double result = integ(f, a, b);
printf("Integral from %f to %f is: %f
", a, b, result);
return 0;
}
この例では、`integ`関数が、関数ポインタを引数として受け取り、積分を計算します。`integ`は、`double`型の引数を取り、`double`型の値を返す関数であれば、どんな関数でも処理できます。
`typedef`を用いることで、関数ポインタの型を定義しておくと便利です。
c
typedef double (func_ptr_type)(double);
double integ(func_ptr_type func, double a, double b) {
...
}
関数ポインタを引数として渡すことで、処理を外部から組み込むことができ、関数を呼び出す側は、実際に何が実行されるかを知る必要はありません。この方法で渡される関数は、コールバック関数と呼ばれることもあります。
C++での関数ポインタ
C++では、以下の関数ポインタはCの関数ポインタと互換性があり、呼び出し規約が同じであれば直接相互運用が可能です。
名前空間レベルのグローバル関数
クラスの静的メンバー関数
ただし、クラスの非静的メンバー関数へのポインタは互換性がありません。また、非静的メンバー関数を呼び出すには、そのクラスのインスタンスが必要です。
C++では関数への参照も定義できますが、非静的メンバー関数への参照は定義できません。
C++では、関数テンプレートの述語として関数オブジェクトやラムダ式が使用されますが、関数ポインタも使用可能です。ただし、関数オブジェクトやラムダ式の方が、コンパイラによるインライン化が期待できるため、好まれる傾向があります。ラムダ式は
C++11で標準化された機能です。
C++11より前のバージョンでは、Boost.Lambdaライブラリを利用することで同様の機能を実現できました。
C++11以降では、`typedef`の代わりに`using`を用いることができ、より分かりやすい構文で型エイリアスを定義できます。
c++
using func_ptr_type = double (*)(double);
上記は、`typedef`の糖衣構文です。
まとめ
関数ポインタは、関数を指し示すポインタであり、柔軟な関数呼び出しや、コールバック関数による処理のカスタマイズを実現するための強力なツールです。
C言語や
C++で利用され、特に
C++では、関数オブジェクトやラムダ式と併用することで、より高度なプログラミングを可能にします。