我试图让一个类函数采取另一个类未知函数。我希望这是有道理的。我想要做的是......如果点击button1(或任何声明的按钮),它将执行输入到函数Event__MouseClicked的第一个参数的任何函数(任何“void function”)。
bool MouseClicked( void ){ /**/ };
namespace XE
{
class Window
{
public:
const char* Title;
};
class Button
{
public:
void Event__MouseClicked( void( *Function )( void ) ) {
if( MouseClicked( ) )
( *Function )( );
}
};
};
class XorrWindow1 : public XE::Window
{
public:
XorrWindow1( void ) {
Initialize( );
}
protected:
~XorrWindow1( void );
private:
XE::Button Button1;
XE::Button Button2;
private:
// EVENT PROTOTYPES
void DoSomething( void );
void RandomFunction( void );
void Initialize( void )
{
// INITIALIZE CONTROLS
// AND OTHER STUFF BELOW
this->Title = "XorrWindow1";
// How can I resolve this problem?
this->Button1.Event__MouseClicked( &XorrWindow1::DoSomething );
this->Button2.Event__MouseClicked( &XorrWindow1::RandomFunction );
};
};
void XorrWindow1::DoSomething( void ) {
::MessageBoxA( NULL, this->Title, "Button1 clicked!", MB_OK );
};
void XorrWindow1::RandomFunction( void ) {
::MessageBoxA( NULL, this->Title, "Button2 clicked!", MB_OK );
};
错误是这样的:
'XE::Button::Event__MouseClicked' : cannot convert parameter 1 from 'void (__thiscall XorrWindow1::* )(void)' to 'void (__cdecl *)(void)'
我完全理解导致错误的原因。但我不知道如何解决它,因为它必须能够采用类Window1的任何未知功能。
答案 0 :(得分:3)
就像Karel Petranek所说,你需要模板来适应所有情况。这是大多数通用方法(STL在任何地方都使用):
template<typename f>
void Event__MouseClicked(f&& func)
{
if( MouseClicked( ) )
func();
}
基本上Event__MouseClicked
接受可调用对象。在你的情况下,你会称之为Marcelo Cantos描述:
Button1.Event__MouseClicked([&]{ DoSomething(); });
但是这种方法的好处是将任何不带参数的可调用对象传递给Event__MouseClicked
将编译并运行。您可以传递函数指针,std::bind
的结果,std::function
,lambda等等。
这比在某些情况下强行接受std::function
更为理想,例如在接受直接函数指针时。
答案 1 :(得分:2)
您不能将成员函数指针作为自由函数指针传递,因为无法捕获this
并根据需要传递它。
这个问题有很多种不同的解决方案。如果你有C ++ 11,最优雅的是传递std::function<void()>
:
virtual void Event__MouseClicked(const std::function<void()>& f) {
if (MouseClicked()) f();
}
⋮
Button1.Event__MouseClicked([&]{ DoSomething(); });
编辑:我几乎忘了提及使用lambdas,你甚至可以取消回调成员函数:
Button1.Event__MouseClicked([&]{ ::MessageBoxA( NULL, this->Title, "Button1 clicked!", MB_OK ); });
因为这不是模板函数,所以可以在派生类中覆盖它。但请注意,GUI框架通常不需要从类Button
派生类型以定制其行为,并且它们也不会在事件的一部分中传递回调。常用模式是按钮保持回调函数并在适当时调用它。 E.g:
class Button {
⋮
public:
void onClicked(std::function<void(Button*)>& f) { clicked_ = f; }
void click() { clicked_(this); }
private:
std::function<void(Button*)> clicked_;
// Internal click handler responds to OS click events.
void handleClickEvent(…) { clicked_(this); }
};
以上不是世界上最好的设计,但它应该让你了解我在说什么。
答案 2 :(得分:1)
您需要使用模板来适应所有可能的类:
template <typename T>
void Event__MouseClicked(T *object, void( T::*Function )( void ) ) {
if( MouseClicked( ) )
( object->*Function )( );
}
使用:
this->Button2.Event__MouseClicked( this, &XorrWindow1::RandomFunction );
请注意,您将无法在子类中覆盖此方法,因为模板化函数不能是虚拟的。您还需要将“this”指针与方法指针一起传递。