2008-09-24 14 views
26

Estoy tratando de pasar una función miembro dentro de una clase a una función que toma un puntero de clase de función miembro. El problema que estoy teniendo es que no estoy seguro de cómo hacer esto correctamente dentro de la clase con este puntero. ¿Alguien tiene sugerencias?¿Cómo se pasa un puntero de función miembro?

Aquí es una copia de la clase que se pasa a la función miembro:

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; 
} 
}; 

La función x.SetButton (...) está contenido en otra clase, donde "objeto" es una plantilla.

void SetButton(int xPos, int yPos, LPCWSTR normalFilePath, LPCWSTR hoverFilePath, LPCWSTR pressedFilePath, int Width, int Height, void (object::*ButtonFunc)()) { 

    BUTTON::SetButton(xPos, yPos, normalFilePath, hoverFilePath, pressedFilePath, Width, Height); 

    this->ButtonFunc = &ButtonFunc; 
} 

Si alguien tiene algún consejo sobre cómo puedo enviar correctamente esta función para poder utilizarla más adelante.

Respuesta

32

Para llamar a una función miembro mediante el puntero, necesita dos cosas: Un puntero al objeto y un puntero a la función. Es necesario tanto en 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; 
} 

A continuación, puede invocar la función usando ambos punteros:

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

No se olvide de pasar el puntero a su objeto a MenuButton::SetButton():

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

'Un puntero a la clase' - ¿debería ser' Un puntero al objeto'? – Vorac

+0

Muchas gracias. No se pudo deducir que la sintaxis es algo así como ((ButtonObj) -> * (ButtonFunc))() – nathanesau

2

En el raro caso de que le sucede que se desarrollo con Borland C++ Builder y no le importa escribir código específico para ese entorno de desarrollo (es decir, el código que no funciona con otra Compiladores C++), puede usar la palabra clave __closure. Encontré un small article about C++Builder closures. Están destinados principalmente para su uso con Borland VCL.

+2

... Esa es una solución bastante oscura. –

+0

Sí, es oscuro. :) Sin embargo, hay un código legado de Delphi y C++ Builder flotando en el terreno corporativo. Lo mejor es ser minucioso. – Parappa

5

¿No estaría mejor servido para usar OO estándar? Defina un contrato (clase virtual) e impleméntelo en su propia clase, luego simplemente pase una referencia a su propia clase y deje que el receptor llame a la función de contrato.

Usando su ejemplo (he cambiado el nombre del método 'test2' a 'buttonAction'):

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; 
    } 
}; 

En el método del receptor, que almacena la referencia a un ButtonContract, a continuación, cuando se desea realizar la la acción del botón simplemente llama al método 'buttonAction' de ese objeto ButtonContract almacenado.

+0

El problema es que la gran cantidad de funciones diferentes que puede invocar un botón hace que esta tarea sea muy tediosa. Probablemente haya entre 75 y 100 funciones que un botón puede llamar. –

+0

Un testMenu * tiene un * ButtonContract (o muchos de ellos), en lugar de una relación * isA *. Este enfoque hace que sea difícil implementar dos botones con la misma acción o botones con diferentes acciones. – mabraham

2

Otros le han dicho cómo hacerlo correctamente. Pero me sorprende que nadie le ha dicho que este código es realmente peligroso:

this->ButtonFunc = &ButtonFunc; 

Desde ButtonFunc es un parámetro, se irá fuera de alcance cuando se devuelve la función. Usted está tomando su dirección. Obtendrá un valor del tipo void (object::**ButtonFunc)() (puntero a un puntero a una función miembro) y asígnelo a este-> ButtonFunc. En el momento en que intentes usar esto-> ButtonFunc intentarás acceder al almacenamiento del parámetro local (que ahora ya no existe), y tu programa probablemente se bloquee.

Estoy de acuerdo con la solución de Commodore.Pero usted tiene que cambiar su línea para

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

desde ButtonObj es un punteroa objetar.

6

Sé que este es un tema bastante antiguo. Pero hay una manera elegante de manejar esto con C++ 11

#include <functional> 

declarar el puntero función como esta

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

declarar su la función de su pase de esta cosa en

void SetHandler(Max Handler); 

Supongamos que le pasa una función normal a la que puede usar como normal

SetHandler(&some function); 

suponga que tiene una función miembro

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

en su código puede pasar usando std::placeholders como esto

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

Gracias por esto. Aunque tienes un tipo: es 'std :: placeholders', plural. – Artfunkel

Cuestiones relacionadas