
Toni das Pony
Wie erhalte ich einen Funktionszeiger für eine Klassenmitgliedsfunktion und rufe diese Mitgliedsfunktion später mit einem bestimmten Objekt auf? Ich möchte 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, was ist der bevorzugte Weg, dies zu tun?

Satbir
Lesen Das für Details:
// 1 define a function pointer and initialize to NULL
int (TMyClass::*pt2ConstMember)(float, char, char) const = NULL;
// C++
class TMyClass
{
public:
int DoIt(float a, char b, char c){ cout << "TMyClass::DoIt"<< endl; return a+b+c;};
int DoMore(float a, char b, char c) const
{ cout << "TMyClass::DoMore" << endl; return a-b+c; };
/* more of TMyClass */
};
pt2ConstMember = &TMyClass::DoIt; // note: <pt2Member> may also legally point to &DoMore
// Calling Function using Function Pointer
(*this.*pt2ConstMember)(12, 'a', 'b');
Wie erhalte ich einen Funktionszeiger für eine Klassenmitgliedsfunktion und rufe diese Mitgliedsfunktion später mit einem bestimmten Objekt auf?
Am einfachsten ist es, mit a anzufangen typedef
. Für eine Member-Funktion fügen Sie den Klassennamen in der Typdeklaration hinzu:
typedef void(Dog::*BarkFunction)(void);
Um dann die Methode aufzurufen, verwenden Sie die ->*
Operator:
(pDog->*pBark)();
Außerdem möchte ich, wenn möglich, den Konstruktor auch über einen Zeiger aufrufen. Ist dies möglich, und wenn ja, was ist der bevorzugte Weg, dies zu tun?
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 geändert, um im Grunde das zu tun, was Sie beschreiben. Es gibt einige Vorbehalte unten.
#include <iostream>
class Animal
{
public:
typedef Animal*(*NewAnimalFunction)(void);
virtual void makeNoise()
{
std::cout << "M00f!" << std::endl;
}
};
class Dog : public Animal
{
public:
typedef void(Dog::*BarkFunction)(void);
typedef Dog*(*NewDogFunction)(void);
Dog () {}
static Dog* newDog()
{
return new Dog;
}
virtual void makeNoise ()
{
std::cout << "Woof!" << std::endl;
}
};
int main(int argc, char* argv[])
{
// Call member function via method pointer
Dog* pDog = new Dog ();
Dog::BarkFunction pBark = &Dog::makeNoise;
(pDog->*pBark)();
// Construct instance via factory method
Dog::NewDogFunction pNew = &Dog::newDog;
Animal* pAnimal = (*pNew)();
pAnimal->makeNoise();
return 0;
}
Jetzt können Sie zwar normalerweise a verwenden Dog*
an Stelle eines Animal*
Dank der Magie des Polymorphismus tut es der Typ eines Funktionszeigers nicht Befolgen Sie die Suchregeln der Klassenhierarchie. Ein Animal-Methodenzeiger ist also nicht mit einem Dog-Methodenzeiger kompatibel, mit anderen Worten, Sie können keinen zuweisen Dog* (*)()
zu einer Variablen vom Typ Animal* (*)()
.
Die Statik newDog
-Methode ist ein einfaches Beispiel für eine Factory, die einfach neue Instanzen erstellt und zurückgibt. Da es sich um eine statische Funktion handelt, hat es eine reguläre typedef
(ohne Klassenkennzeichen).
Nachdem ich das oben Gesagte beantwortet habe, frage ich mich, ob es nicht einen besseren Weg gibt, das zu erreichen, was Sie brauchen. Es gibt ein paar spezifische Szenarien, in denen Sie so etwas tun würden, aber Sie werden vielleicht feststellen, dass es andere Muster gibt, die für Ihr Problem besser funktionieren. Wenn Sie allgemeiner beschreiben, was Sie zu erreichen versuchen, kann sich der Hive-Mind als noch nützlicher erweisen!
Im Zusammenhang mit dem oben Gesagten werden Sie zweifellos die finden Bindung steigern Bibliothek und andere verwandte Module sehr nützlich.
Ich glaube nicht, dass hier jemand erklärt hat, dass ein Problem darin besteht, dass Sie “Mitgliederzeiger” anstelle von normalen Funktionszeigern.
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 nicht kennen, bis Sie wissen, für welches Objekt dereferenziert werden soll (denken Sie an virtuelle Funktionen). Sie müssen auch das Objekt kennen, um das bereitzustellen this
impliziter Parameter natürlich.
Nachdem ich gesagt habe, dass Sie sie brauchen, sage ich jetzt, dass Sie sie wirklich vermeiden müssen. Im Ernst, Mitgliederzeiger sind ein Schmerz. Es ist viel vernünftiger, sich objektorientierte Entwurfsmuster anzusehen, die dasselbe Ziel erreichen, oder eine zu verwenden boost::function
oder was auch immer wie oben erwähnt – vorausgesetzt, Sie können diese Wahl treffen.
Wenn Sie diesen Funktionszeiger an vorhandenen Code liefern, dann wirklich brauchen B. ein einfacher Funktionszeiger, sollten Sie eine Funktion als statisches Mitglied der Klasse schreiben. Eine statische Member-Funktion versteht das nicht this
, also müssen Sie das Objekt als expliziten Parameter übergeben. Es gab einmal eine nicht ganz so ungewöhnliche Redewendung in dieser Richtung für die Arbeit mit altem C-Code, der Funktionszeiger benötigt
class myclass
{
public:
virtual void myrealmethod () = 0;
static void myfunction (myclass *p);
}
void myclass::myfunction (myclass *p)
{
p->myrealmethod ();
}
Seit myfunction
wirklich nur eine normale Funktion ist (Bereichsprobleme beiseite), kann ein Funktionszeiger auf die normale C-Weise gefunden werden.
BEARBEITEN – Diese Art von Methode wird “Klassenmethode” oder “statische Elementfunktion” genannt. Der Hauptunterschied zu einer Nicht-Member-Funktion besteht darin, dass Sie, wenn Sie von außerhalb der Klasse darauf verweisen, den Gültigkeitsbereich mithilfe von angeben müssen ::
Oszilloskopauflösungsoperator. Um beispielsweise den Funktionszeiger zu erhalten, verwenden Sie &myclass::myfunction
und um es Gebrauch zu nennen myclass::myfunction (arg);
.
So etwas ist ziemlich üblich, 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 eher LPARAM oder ähnliches als ein Zeiger, und es ist etwas Casting erforderlich.

Khaled Alshaya
typedef void (Dog::*memfun)();
memfun doSomething = &Dog::bark;
....
(pDog->*doSomething)(); // if pDog is a pointer
// (pDog.*doSomething)(); // if pDog is a reference

Ciro Santilli Путлер Капут 六四事
Minimales lauffähiges Beispiel
main.cpp
#include <cassert>
class C {
public:
int i;
C(int i) : i(i) {}
int m(int j) { return this->i + j; }
};
int main() {
// Get a method pointer.
int (C::*p)(int) = &C::m;
// Create a test object.
C c(1);
C *cp = &c;
// Operator .*
assert((c.*p)(2) == 3);
// Operator ->*
assert((cp->*p)(2) == 3);
}
Kompilieren und ausführen:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out
Getestet unter Ubuntu 18.04.
Sie können die Reihenfolge der Klammern nicht ändern oder weglassen. Folgendes funktioniert nicht:
c.*p(2)
c.*(p)(2)
GCC 9.2 würde fehlschlagen mit:
main.cpp: In function ‘int main()’:
main.cpp:19:18: error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘p (...)’, e.g. ‘(... ->* p) (...)’
19 | assert(c.*p(2) == 3);
|
C++11-Standard
.*
und ->*
Bereich Einzeloperatoren in C++ zu diesem Zweck eingeführt und in C nicht vorhanden.
C++11 N3337-Standardentwurf:
- 2.13 “Operatoren und Satzzeichen” hat eine Liste aller Operatoren, die enthält
.*
und ->*
.
- 5.5 „Pointer-to-Member-Operatoren“ erklärt, was sie tun
Ich bin hierher gekommen, um zu lernen, wie man einen Funktionszeiger (keinen Methodenzeiger) aus einer Methode erstellt, aber keine der Antworten hier bietet eine Lösung. Hier ist, was ich herausgefunden habe:
template <class T> struct MethodHelper;
template <class C, class Ret, class... Args> struct MethodHelper<Ret (C::*)(Args...)> {
using T = Ret (C::*)(Args...);
template <T m> static Ret call(C* object, Args... args) {
return (object->*m)(args...);
}
};
#define METHOD_FP(m) MethodHelper<decltype(m)>::call<m>
Also für Ihr Beispiel würden Sie jetzt tun:
Dog dog;
using BarkFunction = void (*)(Dog*);
BarkFunction bark = METHOD_FP(&Dog::bark);
(*bark)(&dog); // or simply bark(&dog)
Bearbeiten:
Mit C++17 gibt es eine noch bessere Lösung:
template <auto m> struct MethodHelper;
template <class C, class Ret, class... Args, Ret (C::*m)(Args...)> struct MethodHelper<m> {
static Ret call(C* object, Args... args) {
return (object->*m)(args...);
}
};
die direkt ohne das Makro verwendet werden kann:
Dog dog;
using BarkFunction = void (*)(Dog*);
BarkFunction bark = MethodHelper<&Dog::bark>::call;
(*bark)(&dog); // or simply bark(&dog)
Für Methoden mit Modifikatoren wie const
Möglicherweise benötigen Sie weitere Spezialisierungen wie:
template <class C, class Ret, class... Args, Ret (C::*m)(Args...) const> struct MethodHelper<m> {
static Ret call(const C* object, Args... args) {
return (object->*m)(args...);
}
};

Benjamin
Ein Funktionszeiger auf ein Klassenmitglied ist ein Problem, das sich sehr gut für die Verwendung von boost::function eignet. Kleines Beispiel:
#include <boost/function.hpp>
#include <iostream>
class Dog
{
public:
Dog (int i) : tmp(i) {}
void bark ()
{
std::cout << "woof: " << tmp << std::endl;
}
private:
int tmp;
};
int main()
{
Dog* pDog1 = new Dog (1);
Dog* pDog2 = new Dog (2);
//BarkFunction pBark = &Dog::bark;
boost::function<void (Dog*)> f1 = &Dog::bark;
f1(pDog1);
f1(pDog2);
}
9954700cookie-checkAufrufen von C++-Memberfunktionen über einen Funktionszeigeryes
Ich verstehe immer noch nicht wirklich, warum, wenn Sie eine Member-Funktion eines Objekts aufrufen und dann einfach einen Zeiger auf das Objekt übergeben möchten? Wenn sich die Leute darüber beschweren, weil es Ihnen ermöglicht, die Klasse besser zu kapseln, warum nicht eine Schnittstellenklasse erstellen, von der alle Klassen erben?
– Tschad
28. September 2009 um 8:39 Uhr
Es kann bei der Implementierung von so etwas wie dem Befehlsmuster nützlich sein, obwohl viele Leute boost::function verwenden würden, um die Mechanik des rohen Member-Zeigers zu verbergen.
– CB Bailey
28. September 2009 um 8:45 Uhr
Warum weisen Sie diesen Hund dynamisch zu? Anschließend müssen Sie das Objekt ebenfalls manuell löschen. Das sieht sehr danach aus, als ob Sie von Java, C# oder einer anderen vergleichbaren Sprache kommen und immer noch mit C++ kämpfen. Ein einfaches automatisches Objekt (
Dog dog;
) ist eher das, was Sie wollen.– sbi
28. September 2009 um 9:18 Uhr
@Chad: Ich würde meistens zustimmen, aber es gibt Zeiten, in denen das Übergeben einer Referenz teurer wäre. Stellen Sie sich eine Schleife vor, die über eine Art von Daten (Parsing, Berechnung usw.) iteriert, als dass das Aufrufen einer Funktion auf der Grundlage einiger Wenn/Sonst-Berechnungen Kosten verursacht, bei denen das Aufrufen der Pointed-to-Funktion ein solches Wenn/Dann vermeiden könnte /else prüft, ob diese Prüfungen vor Eintritt in die Schleife durchgeführt werden könnten.
– Erich
25. Oktober 2012 um 18:04 Uhr
Siehe auch Funktionszeiger auf Memberfunktion.
– jww
2. August 2019 um 4:54 Uhr