3 Stimmen

C++: Adress einer Member-Funktion (Funktionszeiger)

Ich habe eine Klasse X, die diese Methode hat:

void setRxHandler(void (*h)(int));

Und ich möchte ihr eine Memeber-Funktion übergeben, die in Instanzen der Klasse Y existiert.

void comm_rxHandler(int status);

Ich habe folgendes versucht:

x.setRxHandler(comm_rxHandler) 

Aber ich bekomme den folgenden Kompilierungsfehler (Ich benutze Qt):

Fehler: keine übereinstimmende Funktion für den Aufruf von ‘X::setRxHandler()’

Also, wie kann ich das machen?

Ich habe festgestellt, dass wenn ich comm_rxHandler (Klasse Y) als statisch deklariere, ich keine Fehler habe. Aber ich möchte comm_rxHandler als nicht-statische Methode haben. Außerdem möchte ich, dass die setRxHandler-Methode (Klasse X) generisch ist und nicht klassenspezifisch. Also kann ich die Methode nicht wie folgt deklarieren:

setRxHandler(void (Y::*h)(int))

Wie kann ich das machen? Können Sie mir dabei helfen?

Danke!

1voto

André Caron Punkte 43205

C++ unterstützt keine gebundenen Methoden. Um eine Elementfunktion über einen Funktionszeiger aufzurufen, benötigen Sie zwei Dinge: eine Instanz der Klasse und den Funktionszeiger.

Also ist setRxHandler(void (Y::*h)(int)) fast richtig. Sie müssen es wie folgt deklarieren:

void setRxHandler(Y*, void (Y::*h)(int));

Um setRxHandler() aufzurufen, müssen Sie ihm Argumente wie folgt übergeben:

Y y;
setRxHandler(&y, &Y::comm_rxHandler);

In der Methode setRxHandler() können Sie den Funktionszeiger mit dieser Syntax aufrufen:

void setRxHandler ( Y* y, void (Y::*h)(int) )
{
    // ...
    (y->*h)(0);
    // ...
}

Um allgemein zu funktionieren, müssen Sie den Y Parameter abstrahieren, allerdings ist das schwierig richtig hinzubekommen. Schauen Sie sich Boost.Function an für eine bestehende Implementierung, die diesen Anwendungsfall und viele weitere unterstützt.

1voto

sbi Punkte 211669

Ändern Sie Ihren Rückruf zu diesem:

void setRxHandler(std::function();

Dann können Sie Binder verwenden:

setRxHandler( std::bind(&class_name::comm_rxHandler, obj) );

(std::function und std::bind sind Teil der kommenden nächsten Version des C++-Standards. Es ist sehr wahrscheinlich, dass Ihr Compiler sie bereits hat. Wenn nicht, könnten sie im Namespace std::tr1 vorhanden sein. Wenn alles andere fehlschlägt, finden Sie sie unter boost - wo sie erfunden wurden - als boost::function und boost::bind.)

Sie können jedoch auch Nichtmitglieds- oder statische Funktionen an setRxHandler übergeben, sowie Funktionsobjekte (das ist das Ergebnis von std::bind).

Wenn Ihr Compiler bereits Lambda-Funktionen unterstützt (auch Teil des nächsten Standards, aber bereits von z.B. aktuellen Versionen von GCC und VC unterstützt), können Sie auch eine davon verwenden:

setRxHandler( [](){obj.comm_rxHandler();} );

0voto

Seth Carnegie Punkte 72029

Wie es jetzt ist, nimmt das setRxHandler Prototyp einen Zeiger auf eine Funktion, die nichts zurückgibt und ein int annimmt. Wie Sie bemerkt haben, funktioniert dies nicht mit Memberfunktionen, da sie nicht wie eine normale Funktion aufgerufen werden können (Sie müssen auch den this Zeiger behandeln, was bedeutet, dass Sie eine Instanz dieser Klasse haben müssen, um die Methode aufzurufen).

Um es sowohl mit Memberfunktionen als auch mit nicht-spezifischen (generischen) Funktionen arbeiten zu lassen, müssen Sie eine Basisklasse erstellen und alle Klassen, mit denen Sie setRxHandler verwenden möchten, von dieser Klasse ableiten lassen:

class Base { ... };
class Derived : public Base { ... };

// Dann für den Prototypen
void setRxHandler(void (Base::*h)(int)) { ... }
// und Sie können setRxHandler für alle Typen verwenden, die von Base abgeleitet sind, was Ihnen mehr Kontrolle gibt als die zweite Option, die da lautet:

oder verwenden Sie Templates:

template
void setRxHandler(void (T::*h)(int)) { ... }

Mit der Vorlageoption haben Sie wirklich keine Kontrolle darüber, welche Klasse mit setRxHandler verwendet wird (ausgenommen RTTI), was genau das sein kann, was Sie wollen.

0voto

John Zwinck Punkte 221200

Sie können entweder eine Basisklasse für Y erstellen und diese verwenden (um "klassenspezifisch" zu vermeiden), oder Templates verwenden:

template 
setRxHandler(void (T::*h)(int));

Aber dann könnten Fragen auftauchen, wie die Memberfunktion verwendet werden soll (Sie sagen uns, wenn das der Fall ist).

0voto

Tim Punkte 1336

Wie bereits von anderen erwähnt wurde, bietet C++ diese Funktionalität nicht.

Eine weitere Option, die Sie verwenden könnten, ist libsigc++, die weit verbreitet in gtkmm verwendet wird. Sehen Sie sich dieses Beispiel in ihrem Tutorial an, wie beispielsweise das Übergeben von Zeigern auf Memberfunktionen. Ihr Beispiel könnte wie folgt aussehen:

// sigc::slot ist ein 'Slot', um eine Funktion mit dem Rückgabetyp void und einem int-Argument zu halten.
void setRxHandler(sigc::slot slot);
void comm_rxHandler(int status);

//sigc::mem_fun() kann eine Memberfunktion in eine Funktions-Slot umwandeln.
x.setRxHandler(sigc::mem_fun(*this, &X::comm_rxHandler));

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X