2008-09-24 10 views
26

Ich versuche, eine Memberfunktion innerhalb einer Klasse an eine Funktion zu übergeben, die einen Memberfunktionsklassenzeiger verwendet. Das Problem, das ich habe, ist, dass ich nicht sicher bin, wie man das innerhalb der Klasse mit dem this-Zeiger richtig macht. Hat jemand Vorschläge?Wie übergeben Sie einen Mitgliedsfunktionszeiger?

Hier ist eine Kopie der Klasse, die die Elementfunktion ist vorbei:

class testMenu : public MenuScreen{ 
public: 

bool draw; 

MenuButton<testMenu> x; 

testMenu():MenuScreen("testMenu"){ 
    x.SetButton(100,100,TEXT("buttonNormal.png"),TEXT("buttonHover.png"),TEXT("buttonPressed.png"),100,40,&this->test2); 

    draw = false; 
} 

void test2(){ 
    draw = true; 
} 
}; 

Die Funktion x.SetButton (...) in einer anderen Klasse enthalten ist, in der „Objekt“ eine Vorlage ist.

Wenn jemand einen Rat hat, wie ich diese Funktion richtig senden kann, damit ich sie später verwenden kann.

Antwort

32

Um eine Elementfunktion per Zeiger aufzurufen, benötigen Sie zwei Dinge: Einen Zeiger auf das Objekt und einen Zeiger auf die Funktion. Sie müssen sowohl in MenuButton::SetButton()

template <class object> 
void MenuButton::SetButton(int xPos, int yPos, LPCWSTR normalFilePath, 
     LPCWSTR hoverFilePath, LPCWSTR pressedFilePath, 
     int Width, int Height, object *ButtonObj, void (object::*ButtonFunc)()) 
{ 
    BUTTON::SetButton(xPos, yPos, normalFilePath, hoverFilePath, pressedFilePath, Width, Height); 

    this->ButtonObj = ButtonObj; 
    this->ButtonFunc = ButtonFunc; 
} 

Dann können Sie die Funktion mit beiden Zeiger aufrufen:

((ButtonObj)->*(ButtonFunc))(); 

Vergessen Sie nicht, den Zeiger auf das Objekt zu MenuButton::SetButton() weitergeben müssen:

testMenu::testMenu() 
    :MenuScreen("testMenu") 
{ 
    x.SetButton(100,100,TEXT("buttonNormal.png"), TEXT("buttonHover.png"), 
     TEXT("buttonPressed.png"), 100, 40, this, test2); 
    draw = false; 
} 
+2

'Ein Zeiger auf die Klasse' - sollte das' Ein Zeiger auf das Objekt' sein? – Vorac

+0

Vielen Dank. Konnte nicht herausfinden, dass die Syntax etwas wie ((ButtonObj) -> * (ButtonFunc))() – nathanesau

2

In dem seltenen Fall, dass Sie mit Borland C++ Builder Entwicklung werden passieren und nichts dagegen haben, das Schreiben von Code spezifisch für diese Entwicklungsumgebung (das heißt, Code, der nicht mit anderen arbeiten C++ - Compiler) können Sie das Schlüsselwort __closure verwenden. Ich habe eine small article about C++Builder closures gefunden. Sie sind hauptsächlich für die Verwendung mit Borland VCL vorgesehen.

+2

Das ist eine ziemlich ... obskure Lösung. –

+0

Yup, es ist dunkel. :) Es gibt Delphi und C++ Builder Legacy-Code jedoch im Firmenland. Am besten, gründlich zu sein. – Parappa

5

Wäre es nicht besser, Standard OO zu verwenden? Definieren Sie einen Vertrag (virtuelle Klasse) und implementieren Sie diesen in Ihrer eigenen Klasse. Geben Sie dann einfach einen Verweis auf Ihre eigene Klasse weiter und lassen Sie den Empfänger die Vertragsfunktion aufrufen.

Arbeiten mit dem Beispiel (Ich habe die ‚test2‘ Methode ‚buttonAction‘ umbenannt):

class ButtonContract 
{ 
    public: 
    virtual void buttonAction(); 
} 


class testMenu : public MenuScreen, public virtual ButtonContract 
{ 
    public: 
    bool draw; 
    MenuButton<testMenu> x; 

    testMenu():MenuScreen("testMenu") 
    { 
     x.SetButton(100,100,TEXT("buttonNormal.png"), 
       TEXT("buttonHover.png"), 
       TEXT("buttonPressed.png"), 
       100, 40, &this); 
     draw = false; 
    } 

    //Implementation of the ButtonContract method! 
    void buttonAction() 
    { 
     draw = true; 
    } 
}; 

Im Empfänger Methode speichern Sie die Referenz auf ein ButtonContract, dann, wenn Sie das ausführen möchten Die Aktion der Schaltfläche ruft einfach die Methode 'buttonAction' des gespeicherten ButtonContract-Objekts auf.

+0

Das Problem ist, dass die schiere Anzahl der verschiedenen Funktionen, die durch eine Schaltfläche aufgerufen werden können, diese Aufgabe sehr mühsam machen würde. Es gibt wahrscheinlich 75 bis 100 Funktionen, die eine Taste möglicherweise anrufen kann. –

+0

Ein testMenu * hasA * ButtonContract (oder viele von ihnen), anstatt eine * isA * -Beziehung. Dieser Ansatz macht es schwierig, zwei Schaltflächen mit derselben Aktion oder Schaltflächen mit unterschiedlichen Aktionen zu implementieren. – mabraham

2

Andere haben Ihnen gesagt, wie man es richtig macht. Aber ich bin überrascht, niemand gesagt, dieser Code ist tatsächlich gefährlich:

this->ButtonFunc = &ButtonFunc; 

Da ButtonFunc ist ein Parameter, wird es außerhalb des Gültigkeitsbereichs gehen, wenn die Funktion zurückkehrt. Du nimmst seine Adresse. Sie erhalten einen Wert vom Typ void (object::**ButtonFunc)() (Zeiger auf einen Zeiger auf eine Elementfunktion) und weisen Sie es diesem-> ButtonFunc zu. Zu der Zeit, wo Sie versuchen würden, this-> ButtonFunc zu verwenden, würden Sie versuchen, auf den Speicher des (jetzt nicht mehr vorhandenen) lokalen Parameters zuzugreifen, und Ihr Programm würde wahrscheinlich abstürzen.

Ich stimme der Lösung von Commodore zu.Aber man muss seine Linie ändern zu

((ButtonObj)->*(ButtonFunc))(); 

seit ButtonObj ist ein Zeiger zu widersprechen.

6

Ich weiß, das ist ein ziemlich altes Thema. Aber es ist ein eleganter Weg, dies mit c zu handhaben ++ 11

#include <functional> 

erklären, um Ihre Funktionszeiger wie diese

typedef std::function<int(int,int) > Max; 

erklären sich die Funktion Ihren Pass dieses Ding in

void SetHandler(Max Handler); 

Angenommen, Sie übergeben eine normale Funktion, die Sie wie normal verwenden können

SetHandler(&some function); 

dass Sie eine Memberfunktion

class test{ 
public: 
    int GetMax(int a, int b); 
... 
} 

in Ihrem Code haben Sie es std::placeholders wie diese

mit passieren können
test t; 
Max Handler = std::bind(&test::GetMax,&t,std::placeholders::_1,std::placeholders::_2); 
some object.SetHandler(Handler); 
+1

Danke dafür. Sie haben jedoch einen Typ: es ist 'std :: placeholders', Plural. – Artfunkel