関数のポインタを利用する

SPECIAL

久しぶりに関数のポインタを利用しようとしたらすっかり使い方を忘れてしまっていたので、この機にまとめておくことにしました。


関数のポインタ

Visual C++ に限った話ではないですけど、C 言語には関数のポインタというものがあります。

この関数のポインタを使用すると、変数を使って関数を制御できるので、プログラム中で条件に応じて関数を切り換えるといったことができるようになります。

 

使い方

宣言

関数のポインタは関数へのポインタを格納する変数ですので、int などと同様、使う前に変数を宣言する必要があります。宣言の仕方は次の通りです。

void sample(double x, double y)

{

// 関数へのポインタ pFunction の宣言

double (*pFunction)(double, double);

 

}

 

なれないと少しわかりにくいかもしれませんが、この宣言によって、引数に2つの double 型引数をとり、戻り値として double を返す関数へのポインタ pFunction が宣言されたことになります。

よって、関数のポインタを宣言するためには、その関数の引数と戻り値は決定している必要があります。

 

代入

上記で宣言した pFunction はポインタですから、参照先の関数があってこそ意味があります。このポインタを使用する前に必ず、有効な関数のアドレスを代入する必要があります。

代入方法は簡単で、宣言されている関数の関数名だけを右辺に書いてあげれば大丈夫です。もちろん、ポインタを宣言したときの引数と戻り値が一致している必要はあります。

 

たとえば、2つの整数値を割り算する devision という関数があったとして、それを宣言したポインタに代入するには次のようにします。

double division(double a, double b)

{

return (a / b);

}

 

void sample(double x, double y)

{

// 関数へのポインタ pFunction の宣言

double (*pFunction)(double, double);

 

pFunction = division;

}

このように関数の名前だけを書いて代入すると、関数のポインタに、その名前の関数のアドレスが代入されます。

 

呼び出し

宣言と代入が終わったら、そのポインタを使って関数を呼び出すことができます。

呼び出し方は、変数名の頭にアスタリスク (*) をつけます。通常のポインタのように、アドレスではなく値をとるためにはアスタリスクをつけるのと同じです。

ただし関数の場合は引数や戻り値があるので、すこし雰囲気が変わります。

 

double division(double a, double b)

{

return (a / b);

}

 

void sample(double x, double y)

{

double value;

 

// 関数へのポインタ pFunction の宣言

double (*pFunction)(double, double);

 

pFunction = division;

 

// 関数を呼び出します。

value = (*pFunction)(x, y);

 

}

上のように、関数のポインタが指す値を取り出して、そこへ引数を渡すという感じになります。

上の例では pFunction に対して division という関数のアドレスが代入されていますので、value = (*pFunction)(x, y) という呼び出しは、value = division(x, y) を呼び出しているのと同じことです。

 

引数と戻り値が一緒であればいいので、条件によって実行する関数を変化させるというのにも使用できます。

double division(double a, double b)

{

return (a / b);

}

 

double addition(double a, double b)

{

    return (a + b);

}

 

double subtraction(double a, double b)

{

    return (a - b);

}

 

double multiplication(double a, double b)

{

    return (a * b);

}

 

 

void sample2(double x, double y, char op)

{

double value;

 

// 関数へのポインタ pFunction の宣言

double (*pFunction)(double, double);

 

// 引数の char の値により、関数を変更します。

switch (op)

{

case '+':

pFunction = addition;

break;

 

case '-':

pFunction = subtraction;

break;

 

case '*':

pFunction = multiplication;

break;

 

case '/':

pFunction = division;

break;

}

 

// 関数を呼び出します。

value = (*pFunction)(x, y);

 

}

わざわざ関数のポインタを使うほどの規模ではありませんけど、このようにすることで char 型に渡した演算子の文字から、適した関数を選び出すといったこともできます。

 

関数のポインタを引数に含める

基本的な書き方

今度は、関数の引数へ関数のポインタを渡す方法です。

関数のポインタが変数で取り扱われるわけですから、その変数を別の関数へ渡したい場合、または戻り値にしたい場合もあるでしょう。

 

関数のポインタであれ、int* であれ、char* であれ、取り扱い方はかなり似通っていますので、関数のポインタも同様に、引数の部分に書いてあげることができます。

// ヘッダ部分(どちらでも可)

void sample3(double (*)(double, double));

void sample3(double (*pFunction)(double x, double y));

 

// 実装部分

void sample3(double (*pFunction)(double x, double y))

{

}

 

ヘッダの書き方も実装部分もたいした変わりはないですけど、一応両方とも書いてみることにしました。ヘッダ部分は別に引数に名前をつける必要がないので、引数名ありとなしの二つを書いてみました。

このような感じにすると、引数の pFunction を使って、引数に double を2つとり、結果に double を返す関数へのポインタを受け取ることができます。

 

ただ、ごちゃごちゃしていて非常に見にくいですよね。

また、関数の戻り値として関数のポインタを返すにはどうしたら良いか自分ではわからなかったのですが、詳しい方に教えてもらったところ、次のように書くことで戻り値として関数のポインタを返すことも可能であることがわかりました。

// 関数のポインタ "double *pFunction(double x, double y)" を返す例

double (*sample4())(double, double);

この書き方に慣れていないと、上記の記載で、関数のポインタを返す関数が引数をとるのかとらないのか、また、関数のポインタを返すのか浮動小数点数を返すのか、混乱しそうな感じですけど、この書き方で、"浮動小数点数の引数を 2 つとって 1 つの浮動小数点数を返す関数" へのポインターを返す sample4() という引数をとらない関数を定義することができたことになるようです。

確かに *sample4() の部分を関数名に置き換えてみれば、double SAMPLE4(double, double) のような感じで、返された関数のポインターが期待するコードが描かれます。

 

typedef を利用する

ともあれ関数のポインタの宣言をそのまま書いていては、非常に読みにくいプログラムが出来上がってしまいます。

 そこで、もっと通常の変数の型のように自由に扱えるようにするために、typedef を使った型宣言を行ってみようと思います。

// 関数のポインタ型を宣言

typedef double (*PFUNCTION)(double, double);

このように、typedef の後ろに関数のポインタを宣言する通りに続けると、関数のポインタを意味する型が出来上がります。

型の名前は、関数名に当たる部分です。つまり上記の例ですと、PFUNCTION という関数ポインタの型が定義されたことになります。

 

この型を使って、先ほどあげたプログラムを書き直してみると次のようになります。

// ヘッダ部分(どちらでも可)

void sample3(PFUNCTION);

void sample3(PFUNCTION pFunction);

 

// 実装部分

void sample3(PFUNCTION pFunction))

{

}

通常の型と同じような表記になりますので、非常にわかりやすいコードになりました。呼び出し方は以前の通り、 (*pFunction)(1, 2) などといった感じで呼び出します。

 

// 戻り値に利用した場合

PFUNCTION sample4()

{

return division;

}

また、戻り値に使用する場合も、このように普通に利用できます。