関数ポインタについて特に「速い」ということはありません。実行時に指定された関数を呼び出すことができます。ただし、他の関数呼び出しから得られるものとまったく同じオーバーヘッドがあります (さらに、追加のポインター間接化)。さらに、呼び出す関数は実行時に決定されるため、通常、コンパイラは関数呼び出しをインライン化することはできません。そのため、関数ポインターは、場合によっては、通常の関数呼び出しよりも大幅に遅くなることがあります。
関数ポインターはパフォーマンスとは関係がないため、パフォーマンスを向上させるために使用しないでください。
代わりに、それらは関数型プログラミングのパラダイムに非常にわずかに同意しています。つまり、関数をパラメーターとして渡したり、別の関数で値を返したりすることができます。
簡単な例は、一般的なソート関数です。2 つの要素をどのように並べ替えるかを決定するには、それらを比較する方法が必要です。これは、ソート関数に渡される関数ポインタである可能性があり、実際、c++std::sort()
はまさにそのように使用できます。より小さい演算子を定義しない型のシーケンスを並べ替えるように要求する場合は、比較を実行するために呼び出すことができる関数ポインターを渡す必要があります。
そして、これは私たちを優れた代替手段にうまく導きます. C++ では、関数ポインターに限定されません。代わりにファンクターを使用することがよくあります。つまり、 operator をオーバーロードするクラスで、()
関数であるかのように「呼び出す」ことができます。ファンクターには、関数ポインターに比べていくつかの大きな利点があります。
- それらはより柔軟性を提供します: それらは、コンストラクター、デストラクター、およびメンバー変数を備えた本格的なクラスです。それらは状態を維持でき、周囲のコードが呼び出すことができる他のメンバー関数を公開する場合があります。
- それらはより高速です: その型が関数のシグネチャのみをエンコードする関数ポインターとは異なり (型の変数はint を取り、void を返す任意の
void (*)(int)
関数である可能性があります。どの関数かはわかりません)、ファンクターの型は正確な関数をエンコードします。これを呼び出す必要があります (ファンクターはクラスなので、C と呼びます。呼び出す関数は、常に であることがわかっています)。これは、コンパイラが関数呼び出しをインライン化できることを意味します。これは、ジェネリックを、データ型用に特別に設計されたハンドコーディングの並べ替え関数と同じくらい高速にする魔法です。コンパイラは、ユーザー定義関数を呼び出すオーバーヘッドをすべて排除できます。C::operator()
std::sort
- それらはより安全です: 関数ポインターにはほとんど型安全性がありません。それが有効な関数を指しているという保証はありません。NULL の可能性があります。また、ポインターに関する問題のほとんどは、関数ポインターにも当てはまります。それらは危険で、エラーが発生しやすいものです。
関数ポインター (C) またはファンクター (C++) またはデリゲート (C#) はすべて同じ問題を解決しますが、洗練度と柔軟性のレベルは異なります。これらを使用すると、関数をファーストクラスの値として扱い、通常どおりに渡すことができます。その他の変数。関数を別の関数に渡すと、指定した時間に関数が呼び出されます (タイマーの期限が切れたとき、ウィンドウの再描画が必要なとき、または配列内の 2 つの要素を比較する必要があるとき)。
私が知る限り (そして、私は長い間 Java を扱っていないので、間違っている可能性もあります)、Java には直接の同等物はありません。代わりに、インターフェイスを実装し、関数を定義するクラスを作成する必要があります (Execute()
たとえば、それを呼び出します)。次に、ユーザー提供の関数 (関数ポインター、ファンクター、またはデリゲートの形で) を呼び出す代わりに、 を呼び出しますfoo.Execute()
。原則として C++ 実装に似ていますが、C++ テンプレートの一般性がなく、関数ポインターとファンクターを同じように扱うことができる関数構文がありません。
そこで、関数ポインタを使用します。より洗練された代替手段が利用できない場合 (つまり、C で行き詰まっている場合)、ある関数を別の関数に渡す必要がある場合。最も一般的なシナリオはコールバックです。X が発生したときにシステムが呼び出す関数 F を定義します。したがって、F を指す関数ポインターを作成し、それを問題のシステムに渡します。
本当に、John Carmack のことは忘れて、彼のコードをコピーすれば魔法のようにコードが改善されると思い込まないでください。彼が関数ポインタを使用したのは、あなたが言及したゲームが C で書かれており、優れた代替手段が利用できないためであり、単に存在するだけでコードの実行が高速化される魔法の要素だからではありません。