Как вы передаете указатель на функцию-член?
Я пытаюсь передать функцию-член внутри класса функции, которая принимает указатель класса элемента-члена. Проблема, с которой я сталкиваюсь, заключается в том, что я не уверен, как правильно сделать это в классе с помощью этого указателя. У кого-нибудь есть предложения?
Вот копия класса, передающего функцию-член:
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;
}
Если у кого-нибудь есть какие-либо советы о том, как я могу правильно отправить эту функцию, чтобы впоследствии ее использовать.
Ответы
Ответ 1
Чтобы вызвать функцию-член по указателю, вам понадобятся две вещи: указатель на объект и указатель на функцию. Вам нужно как в 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;
}
Ответ 2
Я бы настоятельно рекомендовал boost::bind
и boost::function
для чего-то вроде этого.
См. Передача и вызов функции-члена (boost:: bind/boost:: function?)
Ответ 3
Я знаю, что это довольно старая тема. Но есть элегантный способ справиться с этим с помощью С++ 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);
Ответ 4
Не лучше ли вам использовать стандартную OO. Определите контракт (виртуальный класс) и реализуйте его в своем собственном классе, а затем просто передайте ссылку на свой собственный класс и позвольте получателю вызвать контрактную функцию.
Используя ваш пример (я переименовал метод '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.
Ответ 5
В редком случае, когда вы разрабатываете Borland С++ Builder и не против писать код, специфичный для этой среды разработки (то есть код, который не будет работать с другими компиляторами на С++), вы можете использовать ключевое слово __closure. Я нашел небольшую статью о закрытии С++ Builder. Они предназначены в первую очередь для использования с Borland VCL.
Ответ 6
Другие говорили вам, как это сделать правильно. Но я удивлен, что никто не сказал вам, что этот код действительно опасен:
this->ButtonFunc = &ButtonFunc;
Так как ButtonFunc является параметром, он выйдет из области действия, когда функция вернется. Вы берете его адрес. Вы получите значение типа void (object::**ButtonFunc)()
(указатель на указатель на функцию-член) и назначьте его этому → ButtonFunc. В то время, когда вы попытаетесь использовать this- > ButtonFunc, вы попытаетесь получить доступ к хранилищу локального параметра (теперь уже не существующего), и ваша программа, вероятно, сбой.
Я согласен с решением Commodore. Но вы должны изменить свою линию на
((ButtonObj)->*(ButtonFunc))();
поскольку ButtonObj является указателем на объект.