関数ポインタ(C++)

4023 ワード

関数ポインタは、ポインタが変数アドレスであるように似ていますが、指す変数が関数を指すようになります.
int nArray[10]は実際には1つのポインタが10個のintを有する配列を指すためである.このポインタを参照するときは、*(nArray+index)またはnArray[index]を使用します.
これはよく使われていますが、配列名はアドレス、つまりポインタです.nArray=int*nArrayです.nArray[1] = *(nArray + 1).
そこで配列名がアドレスであるという考え方に基づいて、関数名はエントリアドレスであるかどうか.
       int  foo(); fooは実際には関数を指すポインタでもあり、関数が呼び出されると()オペレータによって関数ポインタが解引用される.
      1. int(*fPoo)()とint*fPoo()の違い
優先度のため、int(*fPoo)()は、fPooが関数を指すポインタであり、この関数にはパラメータがなく、戻り値が整数であると記述することができる.一方、int*fPoo()は、fPooはパラメータがなく、戻り値が整数を指すポインタである関数であると記述することができる.このことから,int(*fPoo)()ではfPooがどの関数に対しても一致(戻り値,パラメータなど)すればよいことが分かる.
      2. (a pointer to a function)の2つの基本的な状況.
(1)関数を指向するポインタに与える.
                
#include <iostream>
using namespace std;

inline int foo(int nX){ return nX; }

inline void foo() { cout << "function pointer foo" << endl; }
inline void goo() { cout << "function pointer goo" << endl; }


int main()
{
    int (*pFoo)(int) = foo;
    void (*pFun)() = foo;   // pFoo    foo
    pFun();
    pFun = goo;            // pFoo      foo
    pFun();
    cout << pFoo(6);
    return 0;
}

上のコードではっきり書いてありますが、もちろん関数ポインタの使用はパラメータと戻りタイプを一致させます.
実行結果:
function pointer foo
function pointer goo
6
(2)関数ポインタを用いて関数の呼び出しを行う.上記のコードでも、この場合が関数ポインタによる呼び出しであることを説明できます.
もちろん、ここでの呼び出し方式は唯一ではなく、これは暗黙的な呼び出しであり、pFoo(6)である.この呼び出しは通常の関数呼び出しのように見える.
もう1つは表示呼び出し、(*pFoo)(6);
3ポインタを使用して関数を指す理由.
具体的な応用は具体的な実現が必要で、この方法を使うかどうかを見るのが比較的に良い方法で、例を挙げます:この例は以前の本で見たことがあります.似たような考えは、関数を書いてタスクを実行するとき、例えばソートをします.ただし、呼び出し元が実装をカスタマイズしたいタスクの一部(ソートは昇順または降順、奇数の後に偶数など)
例は次のとおりです.
#include <iostream>
#include <algorithm>  // for swap

using namespace std;

template <class T>
void SelectSort(T *nArray, const int nSize, bool (*pCompare)(T, T));

template <class T>
bool Ascending(T nX, T nY);

template <class T>
bool Descending(T nX , T nY);

template <class T>
void PrintArray(T *nArray, const int nIndex);

int main()
{
    const int NUM = 6;
    float anArray[NUM] = { 23.2, 523.2, 224.15, 10.1, 262.6, -562.5 };

    SelectSort(anArray, NUM, Descending);
    cout << "Descending: " << endl;
    PrintArray(anArray, NUM);

    SelectSort(anArray, NUM, Ascending);
    cout << "Ascending: " << endl;
    PrintArray(anArray, NUM);

    return 0;
}

template <class T>
bool Descending(T nX , T nY)
{
    return nX > nY;
}

template <class T>
bool Ascending(T nX, T nY)
{
    return nX < nY;
}

template <class T>
void PrintArray(T *nArray, const int nIndex)
{
    for (int i = 0; i < nIndex; ++i)
        cout << nArray[i] << "\t";
    cout << endl;
}


template <class T>
void SelectSort(T *nArray, const int nSize, bool (*pCompare)(T, T))
{
    int i, j, nIndex;
    for (i = 0; i < nSize; ++i)
    {
        nIndex = i;
        for (j = i + 1; j < nSize; ++j)
        {
            if (pCompare(nArray[j], nArray[nIndex]))
                nIndex = j;
        }
        swap(nArray[i], nArray[nIndex]);
    }
}
運転結果:正しい(略)
もちろん、ここでは昇順と降順の列を柔軟に実現できるだけでなく、実際の必要に応じてソートすることができます.この例では、関数ポインタがコードのreuse能力を大幅に向上させることがわかります.
4.関数ポインタとtypedef
関数ポインタの形式は、特にポインタを使い始めたばかりの人に慣れていないので、typedefを使用して新しいタイプを定義し、関数ポインタを普通の変数のようにします.
    typedef    bool (*pFunTest)  (int   nX);
解釈:新しいタイプを定義します:pFunTest、このタイプは関数を指すポインタで、関数は1つのパラメータと戻り値boolを持っています
次の2つの方法があります.
   (1)
          bool Ok(int   nX,   int    nY,    bool (*pFun) (int   nX)) ;//ちょっと見苦しい
   (2)
bool Ok(int nX,int nY,pFunTest pFun);//nice!!!普通変数のように
以上は個人の愚見であり,関数ポインタの総括でもあり,不足点や理解が不十分な点が避けられない.