関数ポインタ(Function Pointers)


関数ポインタ(Function Pointers)
 
特定のタイプの関数を指すポインタを次のように宣言できます.
void (*fp)(int);      //関数へのポインタ
括弧は、fpがvoid*の値を返す関数ではなく、voidの値を返す関数を指すポインタであることを示す必要があります.通常のデータを指すように、関数を指すポインタは空でもかまいません.そうしないと、適切なタイプの関数を指す必要があります.
たとえば:
extern int f( int );
extern void g( long );
extern void h( int );
//...
fp = f; // error! &f is of type int(*)(int), not void(*)(int)   
fp = g; // error! &g is of type void(*)(long), not void(*)(int) 
fp = 0; // OK, set to null
fp = h; // OK, point to h
fp = &h; // OK, take address explicitly
 
なお、1つの関数のアドレスを1つの関数を指すポインタに初期化または付与する場合、関数アドレスを明示的に取得する必要はなく、コンパイラは関数のアドレスを暗黙的に取得することを知っているので、この場合'&'オペレータはオプションであり、通常は省略しない.
       同様に、コンパイラが参照を解くのに役立つため、関数ポインタが指す関数を呼び出すためにポインタを参照解除する操作も必要ありません.
(*fp)(12); //       (explicit dereference)
fp(12); // (implicit dereference, same result)
 
       void*ポインタが任意のタイプのデータを指すのとは異なり、任意のタイプの関数を指す汎用関数ポインタは存在しません.また、非静的メンバー関数のアドレスはポインタではないため、1つの関数ポインタを非静的メンバー関数に向けることはできません.(詳細については、その他の説明ドキュメントを参照)
 
       関数ポインタの従来の用途の一つはコールバック(callback)を実現することである.コールバックは、将来発生する可能性のあるイベントに反応するように初期化フェーズで設定された場合に呼び出される可能性のある動作である.例えば、私たちが火を消したいなら、事前にどのように反応するかを計画したほうがいいです.
extern void stopDropRoll();
inline void jumpIn() { ... }
//...
void (*fireAction)() = 0;
//...
if( !fatalist ) { // if you care that you're on fire...
    // then set an appropriate action, just in the event!
    if( nearWater )
        fireAction = jumpIn;
    else
        fireAction = stopDropRoll;
}
 
       実行するアクションが決定されると、コードのもう1つの部分は、このアクションが何をしているのかを気にすることなく、いつ実行するかに集中することができます.
if( ftemp >= 451 ) { // if there's a fire...
    if( fireAction ) // ...and an action to execute...
        fireAction(); // ...execute it!
}
 
       1つの関数ポインタがインライン関数(inline function)を指すのは合法であることに注意してください.しかし、コンパイラは、コンパイル中にどの関数が呼び出されるかを正確に決定できないため、関数ポインタによるインライン関数の呼び出しは、インライン式の関数呼び出しを招くことはありません.前の例では、fireActionは2つの関数のいずれかを指す可能性がある(もちろん、両方を指さない場合もある)ため、呼び出しポイントではコンパイラは他に方法がなく、間接的で非インラインの関数呼び出しコードを生成するしかない.
 
       また、関数ポインタがリロード関数のアドレスを持つのも合法です.
void jumpIn();
void jumpIn( bool canSwim );
//...
fireAction = jumpIn;
       ポインタのタイプは、様々な異なる候補関数の中で最適に一致する関数を選択するために使用される.この例ではfireActionのタイプはvoid(*)()であるため、最初のjumpIn関数が選択される.
 
       標準ライブラリでは、コールバックメカニズムとして関数ポインタがいくつか使用されていますが、最も際立っているのは標準関数set_new_handlerはコールバックを設定するために使用されます.グローバルoperator new関数がメモリ割り当て要求を履行できない場合、コールバック関数が呼び出されます.たとえば:
void begForgiveness() {
    logError( "Sorry!" );
    throw std::bad_alloc();
}
//...
std::new_handler oldHandler =
    std::set_new_handler(begForgiveness);
 
       標準タイプ名new_handlerはtypedefです.
       typedef void (*new_handler)();
       したがって、コールバック関数は、パラメータを持たずvoidを返す関数でなければならない.Set_new_handler関数はコールバックをパラメータに設定し、前のコールバックを返します.コールバックを取得および設定するための個別の関数は存在しません.現在のコールバックを得るには、旋回式の慣用手法が必要です.
std::new_handler current
    = std::set_new_handler( 0 ); // get...
std::set_new_handler( current ); // ...and restore!
 
       また、標準関数set_terminateとset_unexpectedでは、このような2つを1つにしたget/setコールバックの習慣的な使い方も使用されています.