Pregunta

Estoy intentando 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 tengo es que no estoy seguro de cómo hacerlo correctamente dentro de la clase usando este puntero. ¿Alguien tiene sugerencias?

Aquí hay una copia de la clase que está pasando 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á contenida 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 usarla más adelante.

¿Fue útil?

Solución

Para llamar a una función miembro mediante un puntero, necesita dos cosas: un puntero al objeto y un puntero a la función. Necesita ambos 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;
}

Luego puedes invocar la función usando ambos punteros:

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

No olvides pasar el puntero a tu 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;
}

Otros consejos

Recomiendo encarecidamente boost :: bind y boost :: function para algo como esto.

Vea Pase y llame a una función miembro (boost :: bind / boost :: function?)

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

#include <functional>

declara tu puntero de función como este

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

declara tu función en la que pasas esto

void SetHandler(Max Handler);

Supongamos que le pasas una función normal, puedes usarla como si fuera normal

SetHandler(&some function);

suponga que tiene una función miembro

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

en tu código puedes pasarlo usando std :: placeholders como este

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

¿No te serviría mejor usar OO estándar? Defina un contrato (clase virtual) e implemente eso 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.

Utilizando 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, almacena la referencia a un ButtonContract, luego, cuando desee realizar la acción del botón, simplemente llame al método 'buttonAction' del objeto ButtonContract almacenado.

En el raro caso de que estés desarrollando con Borland C ++ Builder y no te importe escribir código específico para ese entorno de desarrollo (es decir, código que no funcione con otros compiladores de C ++), puedes usar la palabra clave __closure. Encontré un artículo pequeño sobre los cierres de C ++ Builder . Están diseñados principalmente para su uso con Borland VCL.

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

this->ButtonFunc = &ButtonFunc;

Dado que ButtonFunc es un parámetro, quedará fuera del alcance cuando la función regrese. Usted está tomando su dirección. Obtendrá un valor de tipo void (objeto :: ** ButtonFunc) () ( puntero a un puntero a una función miembro ) y lo asignará a este- > ButtonFunc. En el momento en que intentaría usar este botón - > ButtonFunc, intentaría acceder al almacenamiento del parámetro local (ahora ya no existe), y su programa probablemente fallaría.

Estoy de acuerdo con la solución de Commodore. Pero tienes que cambiar su línea a

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

ya que ButtonObj es un puntero para objetar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top