Wie erhalte ich einen Funktionszeiger für eine Klassenelementfunktion und rufe diese Elementfunktion später mit einem bestimmten Objekt auf? Ich würde gerne schreiben:
class Dog : Animal
{
Dog ();
void bark ();
}
…
Dog* pDog = new Dog ();
BarkFunction pBark = &Dog::bark;
(*pBark) (pDog);
…
Wenn möglich, möchte ich den Konstruktor auch über einen Zeiger aufrufen:
NewAnimalFunction pNew = &Dog::Dog;
Animal* pAnimal = (*pNew)();
Ist dies möglich und wenn ja, wie wird dies bevorzugt?
c++
function-pointers
class-method
Tony das Pony
quelle
quelle
Dog dog;
) ist eher das, was Sie wollen.Antworten:
Lesen Sie dies für Details:
quelle
*this.*pt2Member
funktionieren würde.*
hat einen höheren Vorrang vor.*
... Persönlich hätte ich immer noch geschriebenthis->*pt2Member
, das ist ein Operator weniger.pt2ConstMember
zuNULL
?(object.*method_pointer)
dies der Fall, daher möchten wir, dass die Objekte*
eine höhere Priorität haben.this
wird nur verwendet, um zu demonstrieren, dass alles, worauf Sie sich beziehen,.*
ein Zeiger auf eine Instanz der (Unter-) Klasse sein sollte. Dies ist jedoch eine neue Syntax für mich, die ich nur aufgrund anderer Antworten und Ressourcen vermute, die hier verlinkt sind. Ich schlage eine Bearbeitung vor, um dies klarer zu machen.Es ist am einfachsten, mit einem zu beginnen
typedef
. Für eine Mitgliedsfunktion fügen Sie den Klassennamen in die Typdeklaration ein:Um die Methode aufzurufen, verwenden Sie den
->*
Operator:Ich glaube nicht, dass Sie mit solchen Konstruktoren arbeiten können - ctors und dtors sind etwas Besonderes. Der normale Weg, um so etwas zu erreichen, wäre die Verwendung einer Factory-Methode, die im Grunde nur eine statische Funktion ist, die den Konstruktor für Sie aufruft. Ein Beispiel finden Sie im folgenden Code.
Ich habe Ihren Code so geändert, dass er im Grunde das tut, was Sie beschreiben. Es gibt einige Einschränkungen unten.
Obwohl Sie dank der Magie des Polymorphismus normalerweise ein
Dog*
anstelle einesAnimal*
Dankes verwenden können, folgt der Typ eines Funktionszeigers nicht den Suchregeln der Klassenhierarchie. Ein Animal-Methodenzeiger ist also nicht mit einem Dog-Methodenzeiger kompatibel. Mit anderen Worten, Sie könnenDog* (*)()
einer Variablen vom Typ keine zuweisenAnimal* (*)()
.Die statische
newDog
Methode ist ein einfaches Beispiel für eine Factory, die einfach neue Instanzen erstellt und zurückgibt. Als statische Funktion hat sie eine reguläre Funktiontypedef
(ohne Klassenqualifikation).Nachdem ich das oben Gesagte beantwortet habe, frage ich mich, ob es keinen besseren Weg gibt, um das zu erreichen, was Sie brauchen. Es gibt einige spezifische Szenarien, in denen Sie so etwas tun würden, aber möglicherweise finden Sie andere Muster, die für Ihr Problem besser geeignet sind. Wenn Sie allgemeiner beschreiben, was Sie erreichen möchten, kann sich der Hive-Mind als noch nützlicher erweisen!
In Bezug auf das oben Gesagte werden Sie die Boost-Bindungsbibliothek und andere verwandte Module zweifellos sehr nützlich finden.
quelle
->*
, aber jetzt hoffe ich, dass ich es nie brauchen werde :)Ich glaube nicht, dass hier jemand erklärt hat, dass ein Problem darin besteht, dass Sie " Mitgliedszeiger " anstelle von normalen Funktionszeigern benötigen .
Elementzeiger auf Funktionen sind nicht einfach Funktionszeiger. In Bezug auf die Implementierung kann der Compiler keine einfache Funktionsadresse verwenden, da Sie die aufzurufende Adresse im Allgemeinen erst kennen, wenn Sie wissen, für welches Objekt eine Dereferenzierung erforderlich ist (denken Sie an virtuelle Funktionen).
this
Natürlich müssen Sie auch das Objekt kennen, um den impliziten Parameter bereitzustellen .Nachdem ich gesagt habe, dass Sie sie brauchen, werde ich jetzt sagen, dass Sie sie wirklich vermeiden müssen. Im Ernst, Mitgliederzeiger sind ein Schmerz. Es ist viel vernünftiger, objektorientierte Entwurfsmuster zu betrachten, die das gleiche Ziel erreichen, oder ein
boost::function
oder was auch immer wie oben erwähnt zu verwenden - vorausgesetzt, Sie können diese Wahl treffen, das heißt.Wenn Sie diesen Funktionszeiger für vorhandenen Code bereitstellen, sodass Sie wirklich einen einfachen Funktionszeiger benötigen , sollten Sie eine Funktion als statisches Mitglied der Klasse schreiben. Eine statische Elementfunktion versteht das nicht
this
, daher müssen Sie das Objekt als expliziten Parameter übergeben. Es gab einmal eine nicht allzu ungewöhnliche Redewendung in dieser Richtung für die Arbeit mit altem C-Code, der Funktionszeiger benötigtDa
myfunction
es sich eigentlich nur um eine normale Funktion handelt (abgesehen von Umfangsproblemen), kann ein Funktionszeiger auf normale C-Weise gefunden werden.BEARBEITEN - Diese Art von Methode wird als "Klassenmethode" oder "statische Elementfunktion" bezeichnet. Der Hauptunterschied zu einer Nichtmitgliedsfunktion besteht darin, dass Sie den Bereich mithilfe des
::
Bereichsauflösungsoperators angeben müssen, wenn Sie von außerhalb der Klasse darauf verweisen . Um beispielsweise den Funktionszeiger abzurufen, verwenden Sie&myclass::myfunction
und rufen Sie ihn aufmyclass::myfunction (arg);
.Diese Art von Dingen ist ziemlich häufig, wenn die alten Win32-APIs verwendet werden, die ursprünglich eher für C als für C ++ entwickelt wurden. In diesem Fall ist der Parameter natürlich normalerweise LPARAM oder ähnlich und kein Zeiger, und es ist ein gewisses Casting erforderlich.
quelle
quelle
Minimal lauffähiges Beispiel
main.cpp
Kompilieren und ausführen:
Getestet in Ubuntu 18.04.
Sie können die Reihenfolge der Klammern nicht ändern oder weglassen. Folgendes funktioniert nicht:
GCC 9.2 würde fehlschlagen mit:
C ++ 11 Standard
.*
und->*
sind einzelne Operatoren, die zu diesem Zweck in C ++ eingeführt wurden und in C nicht vorhanden sind.C ++ 11 N3337 Standardentwurf :
.*
und enthält->*
.quelle
Ich bin hierher gekommen, um zu lernen, wie man aus einer Methode einen Funktionszeiger (keinen Methodenzeiger) erstellt, aber keine der Antworten hier bietet eine Lösung. Folgendes habe ich mir ausgedacht:
Für Ihr Beispiel würden Sie jetzt Folgendes tun:
Bearbeiten:
Mit C ++ 17 gibt es eine noch bessere Lösung:
die direkt ohne das Makro verwendet werden kann:
Für Methoden mit Modifikatoren wie
const
benötigen Sie möglicherweise weitere Spezialisierungen wie:quelle
Der Grund, warum Sie keine Funktionszeiger zum Aufrufen von Elementfunktionen verwenden können, ist, dass gewöhnliche Funktionszeiger normalerweise nur die Speicheradresse der Funktion sind.
Um eine Mitgliedsfunktion aufzurufen, müssen Sie zwei Dinge wissen:
Gewöhnliche Funktionszeiger können nicht beide speichern. C ++ - Elementfunktionszeiger werden zum Speichern von a) verwendet. Aus diesem Grund müssen Sie die Instanz beim Aufrufen eines Elementfunktionszeigers explizit angeben.
quelle
Ein Funktionszeiger auf ein Klassenmitglied ist ein Problem, das für die Verwendung der boost :: -Funktion wirklich geeignet ist. Kleines Beispiel:
quelle
Um ein neues Objekt zu erstellen, können Sie entweder die oben erwähnte Platzierung new verwenden oder Ihre Klasse eine clone () -Methode implementieren lassen, die eine Kopie des Objekts erstellt. Sie können diese Klonmethode dann wie oben erläutert mit einem Elementfunktionszeiger aufrufen, um neue Instanzen des Objekts zu erstellen. Der Vorteil des Klons besteht darin, dass Sie manchmal mit einem Zeiger auf eine Basisklasse arbeiten, bei der Sie den Typ des Objekts nicht kennen. In diesem Fall kann eine clone () -Methode einfacher zu verwenden sein. Mit clone () können Sie außerdem den Status des Objekts kopieren, wenn Sie dies wünschen.
quelle
Ich habe das mit std :: function und std :: bind gemacht.
Ich habe diese EventManager-Klasse geschrieben, die einen Vektor von Handlern in einer ungeordneten_Map speichert, die Ereignistypen (die nur const unsigned int sind, ich habe eine große Anzahl von Namespace-Bereichen) einem Vektor von Handlern für diesen Ereignistyp zuordnet.
In meiner EventManagerTests-Klasse habe ich einen Ereignishandler wie folgt eingerichtet:
Hier ist die AddEventListener-Funktion:
Hier ist die EventHandler-Typdefinition:
Dann mache ich in EventManagerTests :: RaiseEvent Folgendes:
Hier ist der Code für EventManager :: RaiseEvent:
Das funktioniert. Ich erhalte den Aufruf in EventManagerTests :: OnKeyDown. Ich muss die Vektoren löschen, die Zeit zum Aufräumen haben, aber wenn ich das mache, gibt es keine Lecks. Das Auslösen eines Ereignisses dauert auf meinem Computer ungefähr 5 Mikrosekunden, was ungefähr 2008 entspricht. Nicht gerade superschnell, aber. Fair genug, solange ich das weiß und es nicht in extrem heißem Code verwende.
Ich möchte es beschleunigen, indem ich meine eigene std :: function und std :: bind rolle und vielleicht ein Array von Arrays anstelle einer ungeordneten Karte von Vektoren verwende, aber ich habe nicht ganz herausgefunden, wie eine Mitgliedsfunktion gespeichert werden soll Zeiger und rufen Sie es aus Code auf, der nichts über die aufgerufene Klasse weiß. Wimpern Antwort sieht sehr interessant aus ..
quelle