Frage

Ich programmiere jetzt schon seit einigen Jahren und habe in bestimmten Fällen Funktionszeiger verwendet.Ich würde gerne wissen, wann es angemessen ist, sie aus Leistungsgründen zu verwenden oder nicht, und zwar im Zusammenhang mit Spielen, nicht mit Unternehmenssoftware.

Funktionszeiger sind schnell, John Carmack hat sie im Quake and Doom-Quellcode in dem Maße verwendet, wie er missbraucht wurde, und weil er ein Genie ist :)

Ich würde gerne häufiger Funktionszeiger verwenden, aber ich möchte sie dort verwenden, wo sie am besten geeignet sind.

Was sind heutzutage die besten und praktischsten Einsatzmöglichkeiten von Funktionszeigern in modernen C-Sprachen wie C, C++, C# und Java usw.?

War es hilfreich?

Lösung

Es gibt nichts besonders „schnell“ über Funktionszeiger. Sie ermöglichen es, eine Funktion aufzurufen, die zur Laufzeit angegeben wird. Aber Sie haben genau den gleichen Aufwand wie auf jedem anderen Funktionsaufruf erhalten würdest (plus der zusätzlichen Zeiger indirection). Da ferner die Funktion zur Laufzeit bestimmt wird, zu nennen, kann der Compiler normalerweise nicht den Funktionsaufruf inline, wie es anderswo kann. Als solche Funktionszeiger in einigen Fällen aufsummieren können als ein normaler Funktionsaufruf deutlich langsamer sein.

Funktionszeiger haben nichts mit Leistung zu tun, und sollte nie gewinnen Leistung verwendet werden.

Stattdessen sind sie eine sehr geringe Anspielung auf das funktionale Programmierparadigma, dass sie ermöglichen es Ihnen, eine Funktion zu übergeben um als Parameter oder Rückgabewert in einer anderen Funktion.

Ein einfaches Beispiel ist eine allgemeine Sortierfunktion. Es muss eine gewisse Art und Weise haben zwei Elemente zu vergleichen, um zu bestimmen, wie sie sortiert werden sollen. Dies könnte ein Funktionszeiger auf die Sortierfunktion, und in der Tat vergangen seine c ++ 's std :: sort () kann so genau verwendet werden. Wenn Sie es fragen Sequenzen eines Typs zu sortieren, wird nicht definiert, die weniger als Operator, müssen Sie in einem Funktionszeiger passieren kann es nennt den Vergleich durchzuführen.

Und das führt uns schön auf eine bessere Alternative. In C ++ sind Sie auf Funktionszeiger nicht auf diese beschränkt. Sie verwenden oft functors statt - das, Klassen, die den Bediener überlasten (), so dass sie „genannt“ werden, als ob sie Funktionen waren. Functors haben ein paar große Vorteile gegenüber Funktionszeiger:

  • Sie bieten mehr Flexibilität: Sie sind vollwertige Klassen, mit Konstruktor, Destruktor und Membervariablen. Sie können Zustand halten, und sie können andere Elementfunktionen, dass ihm das umgebende Code aufrufen kann.
  • Sie sind schneller. Im Gegensatz zu Funktionszeiger, dessen Typ nur die Signatur der Funktion kodieren (eine Variable vom Typ void (*)(int) sein kann jeder Funktion, die einen int übernimmt und void zurückgibt wir nicht wissen können welche), einen Funktor des Typs codiert die genaue Funktion, die aufgerufen werden soll (Da ein Funktor eine Klasse ist, nennen es C, wir wissen, dass die Funktion aufzurufen ist und wird immer sein, C :: operator ()). Und dies bedeutet, dass der Compiler den Funktionsaufruf Inline kann. Das ist die Magie, die die allgemeine std :: sort macht genauso schnell wie Ihre Hand codierte Sortierfunktion speziell für Ihren Datentyp entwickelt. Der Compiler kann alle beseitigt den Aufwand für den Aufruf eine benutzerdefinierte Funktion.
  • Sie sind sicherer: Es gibt sehr wenig Typsicherheit in einem Funktionszeiger. Sie haben keine Garantie, dass es auf eine gültige Funktion verweist. Es könnte NULL sein. Und die meisten der Probleme mit Zeigern gelten auch für Funktionszeiger. Sie sind gefährlich und fehleranfällig.

Funktionszeiger (in C) oder functors (in C ++) oder Teilnehmer (in C #) alle das gleiche Problem zu lösen, mit verschiedenen Ebenen der Eleganz und Flexibilität: Sie ermöglichen es, Funktionen als erstklassige Werte zu behandeln, vorbei an sie um wie jede andere Variable. Sie können eine Funktion auf eine andere Funktion übergeben, und es wird Ihre Funktion zu bestimmten Zeiten aufrufen (wenn ein Timer abläuft, wenn das Fenster neu zu zeichnen muss, oder wenn es benötigt zwei Elemente im Array vergleichen)

Soweit ich weiß (und ich könnte falsch sein, weil ich nicht mit Java seit Jahren gearbeitet haben), Java keine direkte Entsprechung haben. Stattdessen müssen Sie eine Klasse erstellen, die eine Schnittstelle implementiert, und definiert eine Funktion (nennen wir es Execute (), zum Beispiel). Und dann stattdessen den Benutzer bereitgestellte Funktion (in der Form eines Funktionszeiger, Funktors oder delegieren) aufzurufen, rufen Sie foo.Execute (). Ähnlich wie bei der C ++ Implementierung im Prinzip, aber ohne die Allgemeingültigkeit von C ++ Vorlagen und ohne die Funktion Syntax, die Sie Funktionszeiger und functors die gleiche Art und Weise zu behandeln, ermöglicht.

Das ist also, wo Sie Funktionszeiger verwenden: Wenn anspruchsvollere Alternativennicht verfügbar sind (dh. Sie sind in C stecken), und Sie müssen eine Funktion zur anderen zu übergeben. Das häufigste Szenario ist ein Rückruf. Sie definieren eine Funktion F, die das System aufrufen wollen, wenn X passiert. So können Sie einen Funktionszeiger erstellen, um F zeigt, und das betreffende System übergeben.

Also wirklich, über John Carmack vergessen und nicht davon ausgehen, dass alles, was Sie in seinem Code sieht, wird auf magische Weise Ihren Code besser machen, wenn Sie es kopieren. Er benutzte Funktionszeiger, weil die Spiele, die Sie in C geschrieben wurden, erwähnen, wo bessere Alternativen nicht verfügbar sind, und nicht, weil sie schneller eine magische Zutat, deren bloße Existenz macht Code laufen sind.

Andere Tipps

können Sie nützlich sein, wenn Sie die Funktionalität von der Zielplattform bis zur Laufzeit (zum Beispiel CPU-Funktionalität, verfügbarer Speicher) unterstützte nicht kennen. Die offensichtliche Lösung ist es, Funktionen wie folgt zu schreiben:

int MyFunc()
{
  if(SomeFunctionalityCheck())
  {
    ...
  }
  else
  {
    ...
  }
}

Wenn diese Funktion tief in wichtigen Loops genannt wird, sein dann besser wahrscheinlich einen Funktionszeiger für MyFunc zu verwenden:

int (*MyFunc)() = MyFunc_Default;

int MyFunc_SomeFunctionality()
{
  // if(SomeFunctionalityCheck())
  ..
}

int MyFunc_Default()
{
  // else
  ...
}

int MyFuncInit()
{
  if(SomeFunctionalityCheck()) MyFunc = MyFunc_SomeFunctionality;
}

Es gibt auch andere Verwendungen natürlich, wie Callback-Funktionen , Byte-Code ausgeführt von Speicher oder für eine interpretierte Sprache zu schaffen.

Um Intel-kompatibeles Bytecode unter Windows ausführen, die für eine nützlich sein könnte Dolmetscher. Zum Beispiel, hier ist eine stdcall Funktion zurückzukehr 42 (0x2A) in einem Array gespeichert, die ausgeführt werden können:

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);

);

Jedes Mal, wenn Sie einen Event-Handler oder Delegaten in C # verwenden, werden Sie einen Funktionszeiger effektiv verwendet wird.

Und nein, sie sind nicht um Geschwindigkeit. Funktionszeiger sind über Bequemlichkeit.

Jonathan

Tag Diese was die besten und praktische Anwendungen von ganzen Zahlen in der modernen c-Stil Sprachen sind?

Funktionszeiger als Rückrufe in vielen Fällen verwendet. Eine Verwendung ist als Vergleichsfunktion in Sortieralgorithmen. Also, wenn Sie individuelle Objekte vergleichen versuchen, können Sie einen Funktionszeiger auf die Vergleichsfunktion bereitzustellen, die wissen, wie diese Daten zu verarbeiten.

Das heißt, ich werde ein Angebot schaffen, die ich von einem ehemaligen Professor von mir bekommen:

  

behandeln, um eine neue C ++ Funktion, wie Sie eine geladene automatische Waffe in einem überfüllten Raum behandeln würde: es nie nur benutzen, weil es nette aussieht. Warten Sie, bis Sie die Konsequenzen zu verstehen, nicht nett bekommen, schreiben, was Sie wissen, und wissen, was Sie schreiben.

Im trüben, dunklen Zeiten vor C ++, war es ein gemeinsames Muster, das ich in meinem Code verwendet, die eine Struktur mit einer Reihe von Funktionszeigern zu definieren war, dass (in der Regel) auf dieser Struktur in irgendeine Weise betrieben und bereitgestellt, um bestimmte Verhaltensweisen für es. In C ++ Hinsicht war der Bau ich nur einen Vtable. Der Unterschied war, dass ich die Struktur zur Laufzeit Nebeneffekt könnte das Verhalten von einzelnen Objekten im laufenden Betrieb zu ändern, je nach Bedarf. Dies bietet ein viel reicheres Modell der Vererbung auf Kosten der Stabilität und einfache Fehlersuche. Die größt Kosten waren jedoch, dass es genau eine Person war, die effektiv diesen Code schreiben konnte. Me

Ich habe diese stark in einem UI-Framework, das ich die Art und Weise ändern ließ Objekte wurden gemalt, die das Ziel der Kommandos war, und so weiter, on the fly -. Etwas, das nur sehr wenig UIs angeboten

, diesen Prozess in OO-Sprachen formalisierte Nachdem in jeder sinnvollen Art und Weise besser ist.

sprechen nur von C #, aber Funktionszeiger sind über C # verwendet. Die Delegierten und Event (und Lambda-Ausdrücke, usw.) sind alle Funktionszeiger unter der Haube, so fast jedes C # Projekt mit Funktionszeigern gespickt werden wird. Grundsätzlich ist jedes Event-Handler, in der Nähe von jeder LINQ-Abfrage, etc. -. Werden Funktionszeiger mit

Es gibt Gelegenheiten, bei denen Funktionszeiger verwendet, kann Verarbeitung beschleunigen. Einfache Versendetabellen kann anstelle von langen switch-Anweisungen oder if-then-else-Sequenzen verwendet werden.

Funktionszeiger sind ein Versuch des armen Mannes funktionsfähig zu sein. Man könnte sogar ein Argument, dass Funktionszeiger mit einer Sprache funktional macht, da Sie Funktionen höherer Ordnung mit ihnen schreiben kann.

Ohne Verschlüsse und einfache Syntax, sind sie sorta brutto. So neigen Sie dazu, sie zu benutzen, weit weniger als wünschen. Vor allem für „Callback“ -Funktionen.

Manchmal OO-Design arbeitet rund um Funktionen, indem stattdessen einen ganzen Interface-Typen zu schaffen, in der Funktion zu übergeben erforderlich.

C # Schließungen hat, so Funktionszeiger (die ein Objekt tatsächlich speichern, so dass es nicht nur eine rohe Funktion ist, aber getippt Zustand zu) sind erheblich besser benutzbar es.

Bearbeiten Einer der Kommentare sagte, es sollte eine Demonstration der Funktionen höherer Ordnung mit Funktionszeigern sein. Jede Funktion, eine Rückruffunktion zu nehmen ist eine Funktion höherer Ordnung. Wie, sagen wir, EnumWindows :

BOOL EnumWindows(          
    WNDENUMPROC lpEnumFunc,
    LPARAM lParam
);

Der erste Parameter ist die Funktion in passieren, einfach genug. Da es aber keine Schließungen in C ist, werden wir diesen schönen zweiten Parameter erhalten: „Gibt einen anwendungsdefinierten Wert an die Callback-Funktion übergeben werden.“ Der App-definierten Wert kann Sie manuell um untypisierten Zustand übergehen, um mangels Schließungen zu kompensieren.

Der .NET-Framework ist auch mit ähnlichen Entwürfen gefüllt. Zum Beispiel IAsyncResult .AsyncState: „Ruft einen benutzer- definiert oder Objekt, das enthält Informationen über einen asynchronen Betrieb qualifiziert.“ Da die IAR alles, was Sie erhalten auf Ihrem Rückruf ist, ohne Verschlüsse, müssen Sie einen Weg, um einige Daten in den Asynchron-op zu schieben, so dass Sie es später austreiben kann.

Wie pro meine persönliche exprience können sie können Sie erhebliche Codezeilen helfen speichern.

Betrachten Sie die Bedingung: {

switch(sample_var)
{

case 0:
          func1(<parameters>);
          break;

case 1:
          func2(<parameters>);
          break;











up to case n:
                funcn(<parameters>);
                break;

}

wo func1 () ... funcn () Funktionen mit gleichen protype. Was wir tun können, ist: Deklarieren Sie ein Array von Funktionszeigern arrFuncPoint enthält die Adressen der Funktionen func1 () funcn ()

Dann wird der gesamte Schalter Fall würde durch

ersetzt werden

* arrFuncPoint [sample_var];

Funktionszeiger sind schnell

In welchem ​​Kontext?Im Vergleich zu?

Es hört sich so an, als ob Sie Funktionszeiger nur verwenden möchten, um sie zu verwenden.Das wäre schlecht.

Ein Zeiger auf eine Funktion wird normalerweise als Callback oder Event-Handler verwendet.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top