¿Cómo se pasa un puntero de función miembro?


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 usando el puntero this. ¿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á contenido en otra clase, donde "object" 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 tarde.

Author: Carl Seleborg, 2008-09-25

6 answers

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

Entonces puede 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;
}
 32
Author: Commodore Jaeger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-30 16:40:01

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

Ver Pasar y llamar a una función miembro (boost:: bind / boost::function?)

 15
Author: Matt Cruikshank,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:33:28

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

#include <functional>

Declare su puntero de función así

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

Declare su la función su pase esta cosa en

void SetHandler(Max Handler);

Supongamos que le pasa una función normal, puede usarla como normal

SetHandler(&some function);

Supongamos que tiene una función miembro

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

En tu código puedes pasarlo usando std::placeholders así

test t;
Max Handler = std::bind(&test::GetMax,&t,std::placeholders::_1,std::placeholders::_2);
some object.SetHandler(Handler);
 6
Author: Yuanlong Li,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-30 16:12:02

No sería mejor que usaras 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.

Usando tu ejemplo (he renombrado el 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 receiver, almacena la referencia a un ButtonContract, luego, cuando desee realizar la acción del botón, simplemente llame al método' buttonAction ' de ese almacenado Objeto ButtonContract.

 5
Author: GKelly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-25 08:11:18

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

 2
Author: Parappa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-24 23:10:23

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, saldrá del ámbito cuando la función regrese. Estás tomando su dirección. Obtendrá un valor de tipo void (object::**ButtonFunc)() (puntero a un puntero a una función miembro) y asígnelo a this - >ButtonFunc. En el momento en que intentaría usar esto - > ButtonFunc, intentaría acceder al almacenamiento del local (que ya no existe). parámetro, y su programa probablemente se bloquearía.

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

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

Puesto que ButtonObj es un puntero a objeto.

 2
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-23 16:58:18