関数のポインタを使用する - C++ プログラミング

PROGRAM


関数のポインタを使用する方法(C 言語の関数の場合)

C++ では、関数を関数ポインタに代入することができます。

通常の C 関数の場合、関数ポインタ型の変数の定義は、関数を定義するときと同じように記載します。

double (*func)(int, double);

慣れないと分りにくいですが、これが関数ポインタ型の変数 func の定義になります。

書き方としては、ちょうど関数名のところが (*func) に置き換えられている感じです。つまり、逆に読むと func に代入されている値が関数名ということになります。

戻り値の型も、取る引数の型と数も、この表記の中に含まれるので、定義として十分な情報が記載されています。

 

こうして定義した変数には、同じ引数と戻り値を持った関数をそのまま代入することができます。

たとえば、次のような関数が定義されていたとします。

double price(int price, double rate);

このとき、先ほど定義した関数のポインタへ次のようにして代入できます。

func = &price;

このようにして代入した関数ポインタは、次のようにして実行することができます。

double value = (*func)(100, 1.05);

 

オーバーロードされた関数の場合

C++ では関数をオーバーロードできるので、同じ関数名で複数パターンの引数を取るケースがあります。

たとえば、次のように price 関数がオーバーロードされているとします。

bool isValid(int value);

bool isValid(double value);

このとき、単に "isValid" という名前で関数ポインタを取得しようとしても、どちらの関数ポインタを取得しようとしているのか判らなくてコンパイルエラーになる場合があります。

これまでの例のような明示した関数ポインタ変数へ関数を代入するような場合は問題ないのですけど、エラーになるのは auto で指定した関数に関数ポインタを取得しようとした場合や、std::find_if 関数などの引数に渡すような場合です。

 

こんなときには、どちらを取得しようとしているのかを、関数ポインタの型で明示的にキャストしてあげます。

 

オーバーロードされた関数のポインタを auto 変数に代入する場合

たとえば、関数ポインタを auto 変数に取得しようとした場合、関数名だけだと Xcode 4.6.2 の場合は "Variable '*' with type 'auto' has incompatible initializer of type '<overloaded function type>'" というコンパイルエラーが表示されます。

そんなときは、次のように代入元の関数を明示的にキャストします。

auto func = (bool(*)(int))&isValid;

これで、引数に int を取る方の isValid 関数のポインタを取得することができました。

 

std::find_if などの STL 関数の引数に渡す場合

STL でよくある、処理関数を引数に取るような場合、そこにオーバーロードされている関数の名前だけを渡すと、Xcode 4.6.2 では "No matching function for call to '**'" というコンパイルエラーになります。

この場合も次のようにして、引数に渡す関数を明示的にキャストします。

std::find_if(values.begin(), values.end(), (bool(*)(int))isValid);

このようにすることで、引数に int を取る方の isValid 関数を渡すことができます。

 

関数ポインタを使用する方法(C++ クラスのメンバ関数の場合)

C++ クラスのメソッドの場合も C 言語の関数のときとほとんど同じように扱うことができます。

通常のメンバ関数の場合

たとえば CMyClass クラス内で、次のメソッドが定義されていたとします。

double price(int price, double rate);

このとき、このメソッドはクラスのメンバ関数であるため、これを格納するための関数ポインタ変数には、スコープ解決演算子 (::) を使って所属するクラスも併せて指定する必要があります。

double (CMyClass::*func)(int, double);

このようにすることで、次のようにメソッドを代入できます。

func = &CMyClass::price;

このときも、スコープ解決演算子を使って所属するクラスを指定する必要があります。

 

このようにして代入したクラスのメソッドは、インスタンスの情報も添えて実行する必要があります。

double value = (this->*func)(100, 1.05);

今回は this ポインタにあるメソッドを実行している例ですが、このようにどのインスタンスにある関数を呼び出すかを "(this->*func)" という形で指定しています。

このときに括弧でくくるのも重要で、インスタンスと関数を括弧でひとまとめにしてから引数を指定しないとコンパイルエラーになります。

 

const 指定のメンバ関数の場合

C++ クラスのメソッドの場合、読み取り専用のインスタンスでも実行できるメソッドにするために const キーワードが指定されている場合があります。

double price(int price, double rate) const;

このような場合でも、同じ要領で関数ポインタを定義できます。

double (CMyClass::*func)(int, double) const;

このように、所属クラス名とスコープ解決演算子、代入したい関数にもある const をつけるようにします。

 

関数ポインタへの代入は const がつかなかった場合と同じです。

func = &CMyClass::price;

こうすることで、定義した関数ポインタに const が指定されているときには、const が付いた方のメソッドが選ばれて代入されます。

関数ポインタに const をつけていなければ、const が付いたメソッドしかない場合はそれを、const がつかないものがあればそれを優先して、コンパイラが選んで代入してくれます。

 

このようにして代入した const 指定のクラスのメソッドは、const なしのときと同じように実行します。

double value = (this->*func)(100, 1.05);

どのインスタンスにある関数を呼び出すかを "(this->*func)" という形で指定するのは重要で、インスタンスと関数を括弧でひとまとめにしてから引数を指定しないとコンパイルエラーになります。

 

static 指定のメンバ関数の場合

static 指定のメソッドの場合、通常の C 言語の関数のときとほとんど同じになります。

たとえば CMyClass クラス内で、次のメソッドが定義されていたとします。

static double price(int price, double rate);

このとき、このメソッドがクラスのメンバ関数であっても tatic が指定されているときは、関数ポインタにスコープ解決演算子 (::) を含める必要はありません。

double (*func)(int, double);

代入するときはどの関数かを明示しなくてはいけないため、ここだけ、スコープ解決演算子を使って所属クラスを明示します。

func = &CMyClass::price;

 

このようにして代入した static のクラスのメソッドは、通常の C 関数のポインタと同じように実行します。

double value = (*func)(100, 1.05);

クラスのメソッドであっても static 指定のものについては、このようにインスタンスを指定せずに実行できます。

 

オーバーロードされた関数の場合

関数がオーバーライドされていた場合の扱いは、上記の C 言語の関数の場合と同様、目的の関数ポインタ型に明示的にキャストしてあげます。

 

ラムダ関数を使用する方法(C++11 の場合)

コンパイラが C++11 に対応している場合は、ラムダ関数と std::function<T> 型を使って、関数ポインタを扱えるようになっています。

こちらについては ラムダ関数を使用する の方で整理しておきます。


[ もどる ]