C ++将类型作为未知类函数的参数?

时间:2013-01-20 21:23:41

标签: c++

我试图让一个类函数采取另一个类未知函数。我希望这是有道理的。我想要做的是......如果点击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的任何未知功能。

3 个答案:

答案 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”指针与方法指针一起传递。