[gelöst] EventHandler Closures an anderes Objekt als Formular binden
-
Hallo,
normalerweise sind EventHandler Methoden des entsprechenden Formulars. Gibt es eine Möglichkeit, Methoden anderer Objekte an Events zuzuweisen? Sinngemäß sowas:
class TForm1 { ... // Zielbutton TButton* Button; // implementiert void __fastcall OnClick( TObject* Sender ) THandler Handler_; ... };
TForm1::TForm1( TComponent* Owner ) { // OnClick Ereignis an THandler::OnClick binden // wo kommt dann der Objekt Zeiger her? Button->OnClick = &THandler::OnClick }
-
Hallo
Ja, VCL-Events basieren auch nur auf Funktionszeigern.
Allerdings erfordern VCL-Events immer Member-Funktionen, und diese müßen zusätzlich mit __fastcall versehen sein.bis bald
akari
-
DocShoe schrieb:
TForm1::TForm1( TComponent* Owner ) { // OnClick Ereignis an THandler::OnClick binden // wo kommt dann der Objekt Zeiger her? Button->OnClick = &THandler::OnClick }
Das geht so:
THandler* myHandler = ...; Button->OnClick = myHandler->OnClick;
Das sind Methodenzeiger im Delphi-Stil; in C++Builder gibt es dafür das
__closure
-Schlüsselwort; ich würde dieses Konstrukt aber eher als "objektgebundene Methodenzeiger" denn als Closure bezeichnen.Die C++Builder 6-Dokumentation hatte einen sehr ausführlichen Abschnitt über dieses Sprachfeature:
C++Builder 6-Dokumentation schrieb:
__closure
Mit dem Schlüsselwort
__closure
wird ein spezieller Zeigertyp auf eine Elementfunktion deklariert. In Standard-C++ können Sie einen Zeiger auf eine Elementfunktion nur erhalten, wenn Sie den voll qualifizierten Elementnamen verwenden, wie es im folgenden Beispiel getan wird:class base { public: void func(int x) { }; }; typedef void (base::* pBaseMember)(int); int main(int argc, char* argv[]) { base baseObject; pBaseMember m = &base::func; // Hole einen Zeiger auf das Element ‘func’ // Rufe ‘func’ über den Zeiger auf das Element auf (baseObject.*m)(17); return 0; }
Sie können aber einen Zeiger auf ein Element einer abgeleiteten Klasse nicht einem Zeiger auf ein Element einer Basisklasse zuweisen. Diese Regel (auch Kontravarianz genannt) wird im folgenden Beispiel dargestellt:
class derived: public base { public: void new_func(int i) { }; }; int main(int argc, char* argv[]) { derived derivedObject; pBaseMember m = &derived::new_func; // Unzulässig return 0; }
Mit der Schlüsselworterweiterung
_closure
können Sie diese Beschränkung und noch mehr umgehen. Mit einem Closure können Sie einen Zeiger auf eine Elementfunktion für ein Objekt (d.h. eine bestimmte Instanz einer Klasse) bekommen. Das Objekt kann jedes Objekt sein, unabhängig von seiner Vererbungshierarchie. Derthis
-Zeiger des Objekts wird automatisch verwendet, wenn die Elementfunktion über Closure aufgerufen wird. Das folgende Beispiel zeigt, wie ein Closure deklariert und verwendet wird. Es wird davon ausgegangen, daß die Klassen base und derived, die bereits geliefert wurden, definiert sind.int main(int argc, char* argv[]) { derived derivedObject; void (__closure *derivedClosure)(int); derivedClosure = derivedObject.new_func; // Hole einen Zeiger auf ‘new_func’. // Beachte, daß der Closure dem Objekt // ‘derivedObject’ zugeordnet ist. derivedClosure(3); // Rufe ‘new_func’ über den Closure auf. return 0; }
Closures arbeiten auch mit Zeigern auf Objekte, wie Sie in diesem Beispiel sehen können:
void func1(base *pObj) { // Ein Closure nimmt ein int-Argument entgegen und gibt einen void zurück. void ( __closure *myClosure )(int); // Initialisiere den Closure. myClosure = pObj->func; // Verwende den Closure, um die Elementfunktion aufzurufen. myClosure(1); return; } int main(int argc, char* argv[]) { derived derivedObject; void (__closure *derivedClosure)(int); derivedClosure = derivedObject.new_func; // Dasselbe wie vorher... derivedClosure(3); // Wir können Zeiger nutzen, um auch einen Closure zu initialisieren. // Wir können auch einen Zeiger auf ‘func’ in der Basisklasse bekommen. func1(&derivedObject); return 0; }
Beachten Sie, daß wir einen Zeiger auf eine Instanz der derived-Klasse übergeben und daß wir mit ihm einen Zeiger auf eine Elementfunktion in der base-Klasse bekommen. Das ist mit Standard-C++ nicht möglich.
Closures sind ein wichtiger Teil der C++Builder-RAD-Umgebung. Sie geben uns die Fähigkeit, im Objektinspektor einen Ereignis-Handler zuzuweisen. Ein TButton-Objekt hat z.B. ein Ereignis namens OnClick. In der Klasse TButton ist das Ereignis OnClick eine Eigenschaft, die in ihrer Deklaration die
__closure
-Schlüsselworterweiterung verwendet. Das Schlüsselwort__closure
erlaubt es, der Eigenschaft eine Elementfunktion einer anderen Klasse (normalerweise einer Elementfunktion in einem TForm-Objekt) zuzuweisen. Wenn Sie ein TButton-Objekt in ein Formular plazieren und dann ein Handle für das Ereignis OnClick der Schaltfläche erzeugen, erzeugt C++Builder eine Elementfunktion im TForm-Elternteil der Schaltfläche und weist diese Elementfunktion dem Ereignis OnClick auf TButton zu. Auf diese Art wird der Ereignis-Handler mit dieser bestimmten Instanz von TButton und mit keiner anderen assoziiert.
-
audacia schrieb:
Das geht so:
THandler* myHandler = ...; Button->OnClick = myHandler->OnClick;
Das kann manchmal so einfach sein...
Vielen Dank.