の利用の関数ポインタ
-
21-08-2019 - |
質問
私はグを数年間に現在利用されている関数ポインタの場合があります。何を知りたいと思いる場合は適当ではない、これらのパフォーマンス上の理由でというこのゲームは、事業ソフトウェアです。
機能のポインタの速さは、ジョン-Carmack使用してどの程度の濫用の震災は、ドゥームソースコードを入れることを始めたのですが、この天才:)
使う機能のポインタが使いたいのですが、が最も適しています。
これらの日に何が最適かつ実践的な利用の関数ポインタの現代のcスタイルの言語はC,C++、C#、Javaなどが有ります。
解決
あり、特に"fast"のつの関数ポインタ.彼らはできる関数を呼び出する指定を行います。でも全く同様の架していいからその他の機能(プラスに追加のポインタの間接指定).さらに、機能を呼び出しが実行時に決定され、コンパイラでは一般的なインラインの機能として呼び出できます。なお、関数ポインタの場合に追加するく通常の機能ます。
関数ポインタを持たない性能、使うべきではない得る。
代わりに、非常に若干のnodのプログラミングのパラダイムは、そのできるパス機能とパラメータや戻り値は別の機能です。
簡単な例であるが、仕分け機能です。では一部に比較できるよう二つの要素をどのようにすべき全部エクスペディアで。この関数ポインタに渡されるソート機能、c++'s std::sort()を使用できるからないんですけど、そういう.だいソート列のない型を定義は、以下のオペレーター、通関数ポインタで呼び出を行います。
ここにあることを明らかくに優れています。C++でない限定の関数ポインタ.使用functorsではなく、授業に過度な負荷をオペレーター(),できるように"という"しています。Functorsルの大きなメリットの上の関数ポインタ:
- このフレキシビリティの向上:彼らは本格的な授業、コンストラクタのデストラクタは、メンバ変数.いを維持できる状態になるその他の会員機能を、周囲のコードできます。
- い高速:とは異なりの関数ポインタがタイプのみをエンコードの署名機能の変数の型
void (*)(int)
可 他の 機能するintを返します。いかり)は、元の型コードの精密な機能を呼び出す必要があ(元は、クラスの話でしているとしている機能を呼び出しは、常、C::オペレーター()).このコンパイラでインラインの機能ます。それが障害であるという認識の一般std::うっと手にコードソート機能設計を具体的にごdatatype.のコンパイラを排除することができすべてのオーバーヘッドの呼び出しユーザ定義関数。 - その安全性:が非常に少ないタイプの安全を確保する関数ポインタです。する保証はないでポイントに有効な機能です。ではNULLになります。の問題点のポインタの適用の関数ポインタです。彼らに危険なエラーが発生しやすい.
関数ポインタ(C)またはfunctors(C++)や代表者(C#)すべて解決し、同じ問題は、異なるレベルの優雅さと柔軟性ださい扱いとして機能し、最初のクラスの価値を、その周りとして他の変数となります。を渡すことができる機能を他の機能でにお電話の機能で指定した時、タイマーの満了日、ウィンドウズを再描画が必要です"の二つの要素配列)
ってください(とをとることができると思いますか間違っていないたJava for ages)は、Javaないの直接の等価です。その代わり、を作成する必要があるクラスを実装するインターフェースは、定義関数(ここではExecute()など)を行います。その代わりに呼び出し、ユーザによって提供された機能(形状関数のポインター、元または代理人)が呼び出すfoo.Execute().と同様のC++の実施を原則としてでなく、一般のC++テンプレートには、機能文法ることを可能にする処理の関数ポインタとfunctors、同様に進めていく考えです。
その場ご利用の関数ポインタ:がより洗練された選択肢はご利用になれませんが打ち出されている。-おにこだわったC)を渡す必要がありま一機能です。最も普通のシナリオでは、コールバック.定義する関数FのたシステムがXとしての対象になるときがある。で作成する関数ポインタを指Fおよびそのシステム。
でも忘れ、つジョンCarmack、まったく想定していないことではなく、何でも見て彼のコードは魔法の作りコードさまコピーします。その機能のポインタでのゲームだったC言語で記述され、優れた選択肢はないのであ魔法の成分がわずかに存在するコードの実行が速くなります。
他のヒント
できなければならないときに便利でわからない機能をサポートされている対象プラットフォームで実行時の例CPUの機能をご利用いたしました。のかは、シンプルにわかりやすく書く機能のこのように:
int MyFunc()
{
if(SomeFunctionalityCheck())
{
...
}
else
{
...
}
}
この関数が呼び出されるまで、深部の重要なループにそれを用いる関数ポインタのためのMyFunc:
int (*MyFunc)() = MyFunc_Default;
int MyFunc_SomeFunctionality()
{
// if(SomeFunctionalityCheck())
..
}
int MyFunc_Default()
{
// else
...
}
int MyFuncInit()
{
if(SomeFunctionalityCheck()) MyFunc = MyFunc_SomeFunctionality;
}
がどのように コールバック機能, され、実施のバイトコードからメモリを解釈されます。
実 インテル互換バイトコード Windowsの場合は、有料で通訳が同行します。例えば、ここではstdcall機能を返42(0x2A)配列で格納される実行できます:
code = static_cast<unsigned char*>(VirtualAlloc(0, 6, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
// mov eax, 42
code[0] = 0x8b;
code[1] = 0x2a;
code[2] = 0x00;
code[3] = 0x00;
code[4] = 0x00;
// ret
code[5] = 0xc3;
// this line executes the code in the byte array
reinterpret_cast<unsigned int (_stdcall *)()>(code)();
...
VirtualFree(code, 6, MEM_RELEASE);
);
あなたがC#でイベントハンドラまたはデリゲートを使用するときはいつでも、あなたが効果的に関数ポインタを使用しています。
そして、いや、彼らはスピードではないです。関数ポインタは、利便性に関するものです。
ジョナサン
これらの日は、近代的なCスタイルの言語での整数の最良かつ最も実用的な用途は何ですか?
関数ポインタは、多くの場合、コールバックとして使用されています。 1つの用途は、ソートアルゴリズムの比較機能としてあります。あなたがカスタマイズされたオブジェクトを比較しようとしているのであれば、あなたはそのデータを処理する方法を知っている比較関数に関数ポインタを提供することができます。
私は私が私の元教授からもらった見積もりを提供します、言った:
あなたのような新しいC ++機能が混雑した部屋にロードされた自動武器を扱っていましたトリート:それは気の利い見えるのでそれを使用することはありません。あなたが結果を理解するまで、キュート取得あなたが知っていることを書き、そしてあなたが書いたのか分からない、待ってます。
は、薄暗いで、暗黒時代は、C ++の前に、(通常は)のために特定の動作を何らかの方法でその構造体上で動作し、提供した関数ポインタのセットを用いて構造体を定義することであった私は私のコードで使用される一般的なパターンがありましたそれ。 C ++用語では、私だけのvtableを構築しました。違いは、私は副作用をできることが構造体を、実行時に必要に応じてその場で個々のオブジェクトの振る舞いを変更することでした。これは、安定性とデバッグのしやすさを犠牲にし、継承の多くの豊かなモデルを提供しています。最大のコストは、しかし、効果的にこのコードを書くことができ、正確に1人があったことだった:私は
私は、私はその場で、その上のコマンドの対象となった、とオブジェクトが描かれてしまった道を、変更できUIフレームワークの中で頻繁にこれを使用 - 。非常に少数のUIが提供何かを
OO言語で正式にこのプロセスは、すべて意味のある方法で良いです持っています。
だけのC#と言えば、しかし、関数ポインタは、すべてのC#上で使用されています。代表者とイベント(およびラムダ、など)ので、ほぼすべてのC#プロジェクトは、関数ポインタだらけされようとしている、ボンネットの下にあるすべての関数ポインタです。基本的にはすべてのイベントハンドラ、すべてのLINQクエリに近い、など - 。関数ポインタを使用することになります。
機会があります。シンプルなディスパッチテーブルが長いswitch文の代わりに使用したり、されたif-then-elseシーケンスすることができます。
関数ポインタは、機能的であるように貧乏人の試みです。あなたは彼らと高階関数を書くことができますので、あなたも、関数ポインタを有する言語が機能せていること引数を作ることができます。
閉鎖や簡単な構文がなければ、彼らはみかんグロスです。だから、望ましいよりもはるかに少ない、それらを使用する傾向があります。主に「コールバック」機能のために。
時々、オブジェクト指向設計が必要な機能に渡す代わりに、全体のインターフェイスタイプを作成することで機能を使用して回避します。
(それだけで生の機能が、あまりにも入力された状態ではありませんので、実際にオブジェクトを保存する)関数ポインタが大幅により使用できるようにするためのC#は、クロージャを持っています。
編集 コメントの一つは、関数ポインタと高次機能のデモンストレーションがあるはずと述べました。コールバック関数を取る任意の関数は、高階関数です。同様に、たとえば、 EnumWindowsするます:
BOOL EnumWindows(
WNDENUMPROC lpEnumFunc,
LPARAM lParam
);
最初のパラメータは、十分に簡単に渡すための機能です。しかし、Cにはクロージャを存在しないので、我々はこの素敵な二番目のパラメータを取得する:「コールバック関数に渡されるアプリケーション定義の値を指定します。」そのアプリに定義された値は、手動で閉鎖の不足を補うために型指定されていない状態の周囲を通過することができます。
.NETフレームワークも、同様のデザインで満たされています。たとえば、たIAsyncResult の.AsyncState:「USER-を取得します。資格または非同期操作についての情報を含むオブジェクトを定義しました。」 IARはあなたのコールバックに乗るすべてであるので、閉鎖せずに、あなたが後でそれを追い出すことができるように非同期OPにいくつかのデータを突き出すする方法が必要です。
って、私の個人exprienceできなりを行います。
検討条件:{
switch(sample_var)
{
case 0:
func1(<parameters>);
break;
case 1:
func2(<parameters>);
break;
up to case n:
funcn(<parameters>);
break;
}
がfunc1()...funcn()の機能を同protype.何ができるかというのは:を宣言するの配列の関数ポインタ arrFuncPoint を含むアドレスの機能 func1() へ funcn()
その後、全体のスイッチの場合することを義
*arrFuncPoint[sample_var];
関数ポインタが高速です。
どの文脈では?比較すると?
あなただけそれらを使用するために関数ポインタを使用するようにこれが鳴ります。それは悪いでしょう。
関数へのポインタは、通常、コールバックまたはイベントハンドラとして使用される。