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