2013-01-07 8 views
49

Ich weiß, dass dies so oft gefragt wurde, und deshalb ist es schwierig, durch die Gruft zu graben und ein einfaches Beispiel dafür zu finden, was funktioniert.C++ - Klasse Mitglied Rückruf einfache Beispiele

ich das habe, dann ist es einfach und es funktioniert für MyClass ...

#include <iostream> 
using std::cout; 
using std::endl; 

class MyClass 
{ 
    public: 
     MyClass(); 
     static void Callback(MyClass* instance, int x); 
    private: 
     int private_x; 
}; 

class EventHandler 
{ 
    public: 
     void addHandler(MyClass* owner) 
     { 
      cout << "Handler added..." << endl; 
      //Let's pretend an event just occured 
      owner->Callback(owner,1); 
     } 
}; 

EventHandler* handler; 

MyClass::MyClass() 
{ 
    private_x = 5; 
    handler->addHandler(this); 
} 

void MyClass::Callback(MyClass* instance, int x) 
{ 
    cout << x + instance->private_x << endl; 
} 

int main(int argc, char** argv) 
{ 
    handler = new EventHandler(); 
    MyClass* myClass = new MyClass(); 
} 

class YourClass 
{ 
    public: 
     YourClass(); 
     static void Callback(YourClass* instance, int x); 
}; 

Wie das so umgeschrieben werden kann EventHandler::addHandler() werden beide arbeiten mit MyClass und YourClass. Es tut mir leid, aber es ist nur die Art, wie mein Gehirn funktioniert, ich muss ein einfaches Beispiel dafür sehen, was funktioniert, bevor ich verstehen kann, warum/wie es funktioniert. Wenn Sie einen bevorzugten Weg haben, diese Arbeit jetzt zu machen, ist es an der Zeit, sie zu zeigen, bitte markieren Sie diesen Code und senden Sie ihn zurück.

[Bearbeiten]

Es beantwortet wurde, aber die Antwort gelöscht wurde, bevor ich das Häkchen geben könnte. Die Antwort in meinem Fall war eine Vorlagenfunktion. Changed addHandler dazu ...

class EventHandler 
{ 
    public: 
     template<typename T> 
     void addHandler(T* owner) 
     { 
      cout << "Handler added..." << endl; 
      //Let's pretend an event just occured 
      owner->Callback(owner,1); 
     } 
}; 
+2

Wer hat das Template-Beispiel veröffentlicht? Du hast das Häkchen, aber du hast deine Antwort gelöscht, während ich getestet habe. Es hat genau das gemacht, was ich brauchte. Eine einfache Funktionsschablone ist im Eintopf von allen anderen Informationen verloren gegangen, die ich las. Ihre Antwort wurde zur Frage hinzugefügt. – BentFX

+0

Ich denke, es war JaredC. Sie müssen ihn vielleicht jagen = P – WhozCraig

Antwort

115

Statt statische Methoden zu haben und, das um einen Zeiger auf die Klasseninstanz, Sie Funktionalität in dem neuen C++ 11-Standard verwenden: std::function und std::bind:

#include <functional> 
class EventHandler 
{ 
    public: 
     void addHandler(std::function<void(int)> callback) 
     { 
      cout << "Handler added..." << endl; 
      // Let's pretend an event just occured 
      callback(1); 
     } 
}; 

Die addHandler Methode akzeptiert jetzt ein std::function Argument, und dieses "Funktionsobjekt" hat keinen Rückgabewert und nimmt eine Ganzzahl als Argument.

Um es auf eine bestimmte Funktion zu binden, verwenden Sie std::bind:

class MyClass 
{ 
    public: 
     MyClass(); 

     // Note: No longer marked `static`, and only takes the actual argument 
     void Callback(int x); 
    private: 
     int private_x; 
}; 

MyClass::MyClass() 
{ 
    using namespace std::placeholders; // for `_1` 

    private_x = 5; 
    handler->addHandler(std::bind(&MyClass::Callback, this, _1)); 
} 

void MyClass::Callback(int x) 
{ 
    // No longer needs an explicit `instance` argument, 
    // as `this` is set up properly 
    cout << x + private_x << endl; 
} 

Sie müssen std::bind verwenden, wenn die Prozedur hinzuzufügen, wie Sie explizit die sonst implizit this Zeiger als Argument angeben muss.Wenn Sie eine freistehende Funktion haben, müssen Sie nicht std::bind verwenden:

void freeStandingCallback(int x) 
{ 
    // ... 
} 

int main() 
{ 
    // ... 
    handler->addHandler(freeStandingCallback); 
} 

Mit der Event-Handler std::function Objekte verwenden, ermöglicht es auch, die neue C++ 11 lambda functions zu verwenden:

handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; }); 
+3

Danke Joachim! Dieses Beispiel macht viel zur Entmystifizierung von std :: function und std :: bind. Ich werde es sicher in Zukunft verwenden! [Bearbeiten] Ich bekomme immer noch kein Lambda :) – BentFX

+0

Die Template-Klasse tut genau das, was ich brauche, aber die statischen Funktionen und die Instanz-Pointer sorgen nicht für gutes Lesen. :) Je mehr ich mir das ansehe, desto besser verstehe ich es, und wer weiß, vielleicht ist es nur die Zukunft. – BentFX

+0

@BentFX Gern geschehen! :) –

2

Was Sie tun möchten, ist eine Schnittstelle zu machen, die diesen Code behandelt und alle Klassen die Schnittstelle implementieren.

class IEventListener{ 
public: 
    void OnEvent(int x) = 0; // renamed Callback to OnEvent removed the instance, you can add it back if you want. 
}; 


class MyClass :public IEventListener 
{ 
    ... 
    void OnEvent(int x); //typically such a function is NOT static. This wont work if it is static. 
}; 

class YourClass :public IEventListener 
{ 

Beachten Sie, dass für diese die „Callback“ Funktion zu arbeiten, ist nicht statisch, welche ich glaube eine Verbesserung. Wenn Sie wollen, dass es statisch ist, müssen Sie es tun, wie JaredC mit Vorlagen vorschlägt.

0

MyClass und YourClass könnten beide von SomeonesClass abgeleitet werden, die eine abstrakte (virtuelle) Callback Methode hat. Ihr addHandler würde Objekte des Typs SomeonesClass und MyClass akzeptieren und YourClass kann Callback überschreiben, um ihre spezifische Implementierung des Rückrufverhaltens bereitzustellen.

+0

Für was ich tue, spielte ich mit dieser Idee. Aber wegen der vielen verschiedenen Klassen, die meinen Handler benutzen würden, habe ich das nicht als Option gesehen. – BentFX

-1

Wenn Sie Rückrufe mit unterschiedlichen Parametern haben, können Sie Vorlagen verwenden, wie folgt:
// mit kompilieren: g ++ -std = C++ 11 myTemplatedCPPcallbacks.cpp -o myTemplatedCPPcallbacksApp

#include <functional>  // c++11 

#include <iostream>  // due to: cout 


using std::cout; 
using std::endl; 

class MyClass 
{ 
    public: 
     MyClass(); 
     static void Callback(MyClass* instance, int x); 
    private: 
     int private_x; 
}; 

class OtherClass 
{ 
    public: 
     OtherClass(); 
     static void Callback(OtherClass* instance, std::string str); 
    private: 
     std::string private_str; 
}; 

class EventHandler 
{ 

    public: 
     template<typename T, class T2> 
     void addHandler(T* owner, T2 arg2) 
     { 
      cout << "\nHandler added..." << endl; 
      //Let's pretend an event just occured 
      owner->Callback(owner, arg2); 
     } 

}; 

MyClass::MyClass() 
{ 
    EventHandler* handler; 
    private_x = 4; 
    handler->addHandler(this, private_x); 
} 

OtherClass::OtherClass() 
{ 
    EventHandler* handler; 
    private_str = "moh "; 
    handler->addHandler(this, private_str); 
} 

void MyClass::Callback(MyClass* instance, int x) 
{ 
    cout << " MyClass::Callback(MyClass* instance, int x) ==> " 
     << 6 + x + instance->private_x << endl; 
} 

void OtherClass::Callback(OtherClass* instance, std::string private_str) 
{ 
    cout << " OtherClass::Callback(OtherClass* instance, std::string private_str) ==> " 
     << " Hello " << instance->private_str << endl; 
} 

int main(int argc, char** argv) 
{ 
    EventHandler* handler; 
    handler = new EventHandler(); 
    MyClass* myClass = new MyClass(); 
    OtherClass* myOtherClass = new OtherClass(); 
} 
+1

Können Sie bitte erklären, was Sie getan haben, um das Problem des OP zu lösen? Ist es wirklich notwendig, den vollständigen Code des OP einzubinden? Der OP wollte, dass sein Code mit seiner 'YourClass' funktioniert. Sie scheinen diese Klasse entfernt zu haben und haben eine andere 'OtherClass' hinzugefügt. Darüber hinaus hat die Frage bereits eine gute Antwort erhalten.In wie weit ist Ihre Lösung besser, so dass es sich lohnt, sie zu veröffentlichen? – honk

+0

Ich habe nicht gesagt Mein Beitrag ist eine bessere Lösung. Ich habe gezeigt, wie man "OtherClass" in einer Template-Art verwendet. – mohDady

3

Hier ist eine kurze Version, die mit Klassenmethoden Rückrufe und mit regulären Funktionsrückrufen arbeitet. Um in diesem Beispiel zu zeigen, wie Parameter behandelt werden, benötigt die Callback-Funktion zwei Parameter: bool und int.

class Caller { 
    template<class T> void addCallback(T* const object, void(T::* const mf)(bool,int)) 
    { 
    using namespace std::placeholders; 
    callbacks_.emplace_back(std::bind(mf, object, _1, _2)); 
    } 
    void addCallback(void(* const fun)(bool,int)) 
    { 
    callbacks_.emplace_back(fun); 
    } 
    void callCallbacks(bool firstval, int secondval) 
    { 
    for (const auto& cb : callbacks_) 
     cb(firstval, secondval); 
    } 
private: 
    std::vector<std::function<void(bool,int)>> callbacks_; 
} 

class Callee { 
    void MyFunction(bool,int); 
} 

//then, somewhere in Callee, to add the callback, given a pointer to Caller `ptr` 

ptr->addCallback(this, &Callee::MyFunction); 

//or to add a call back to a regular function 
ptr->addCallback(&MyRegularFunction); 

Dies schränkt den C++ 11-spezifischen Code zum addCallback Verfahren und privaten Daten in der Klasse Caller. Das minimiert zumindest die Chance, bei der Implementierung Fehler zu machen.

Verwandte Themen