Как вы передаете указатель на функцию-член? - PullRequest
26 голосов
/ 25 сентября 2008

Я пытаюсь передать функцию-член в классе функции, которая принимает указатель на класс функции-члена. У меня проблема в том, что я не уверен, как правильно сделать это в классе, используя указатель this. У кого-нибудь есть предложения?

Вот копия класса, передающего функцию-член:

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

Функция x.SetButton (...) содержится в другом классе, где «объект» - это шаблон.

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

Если у кого-нибудь есть совет, как правильно отправить эту функцию, чтобы я мог использовать ее позже.

Ответы [ 6 ]

32 голосов
/ 25 сентября 2008

Чтобы вызвать функцию-член по указателю, вам нужны две вещи: указатель на объект и указатель на функцию. Вы должны оба в 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;
}

Затем вы можете вызвать функцию, используя оба указателя:

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

Не забудьте передать указатель на ваш объект на 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;
}
15 голосов
/ 25 сентября 2008

Я бы настоятельно рекомендовал boost::bind и boost::function для чего-либо подобного.

См. Передать и вызвать функцию-член (boost :: bind / boost :: function?)

6 голосов
/ 15 апреля 2015

Я знаю, что это довольно старая тема. Но есть элегантный способ справиться с этим с помощью c ++ 11

#include <functional>

объявите указатель вашей функции следующим образом

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

объявите вашу функцию передать эту вещь в

void SetHandler(Max Handler);

предположим, что вы передали ему обычную функцию, которую вы можете использовать как обычно

SetHandler(&some function);

предположим, у вас есть функция-член

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

в вашем коде вы можете передать его, используя std::placeholders, как это

test t;
Max Handler = std::bind(&test::GetMax,&t,std::placeholders::_1,std::placeholders::_2);
some object.SetHandler(Handler);
5 голосов
/ 25 сентября 2008

Не лучше ли вам использовать стандартные ОО. Определите контракт (виртуальный класс) и реализуйте его в своем собственном классе, затем просто передайте ссылку на свой собственный класс и позвольте получателю вызвать функцию контракта.

Используя ваш пример (я переименовал метод 'test2' в '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;
    }
};

В методе приемника вы сохраняете ссылку на ButtonContract, затем, когда вы хотите выполнить действие кнопки, просто вызовите метод buttonAction этого сохраненного объекта ButtonContract.

2 голосов
/ 23 ноября 2008

Другие говорили вам, как это сделать правильно. Но я удивлен, что никто не сказал вам, что этот код на самом деле опасен:

this->ButtonFunc = &ButtonFunc;

Поскольку ButtonFunc является параметром, он выйдет из области видимости, когда функция вернется. Вы берете его адрес. Вы получите значение типа void (object::**ButtonFunc)() ( указатель на указатель на функцию-член ) и присвоите его this-> ButtonFunc. В то время, когда вы пытаетесь использовать this-> ButtonFunc, вы пытаетесь получить доступ к хранилищу локального (уже не существующего) параметра, и ваша программа, вероятно, вылетит.

Я согласен с решением Коммодора. Но вы должны изменить его строку на

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

, поскольку ButtonObj является указателем на объект.

2 голосов
/ 25 сентября 2008

В редком случае, когда вы разрабатываете с Borland C ++ Builder и не возражаете против написания кода, специфичного для этой среды разработки (то есть кода, который не будет работать с другими компиляторами C ++), вы можете использовать ключевое слово __closure. Я нашел небольшую статью о замыканиях C ++ Builder . Они предназначены в основном для использования с Borland VCL.

...