0

我正在尝试根据此处最佳答案中的链接代码实现事件管理器: 游戏对象相互交谈

但是,当我尝试注册回调时出现错误。我确信它与 typedef 有关,我承认我不确定它是如何工作的,但它在链接代码中的形式完全相同。B类应该是继承自Interface,那为什么类型不同呢?我已将代码压缩为下面的最小示例。

#include <iostream>

class Interface;
typedef void (Interface::*Callback)(void *data);

class Interface
{
    public:
        void Register    (Callback func);

};

void Interface::Register(Callback func)
{
    std::cout << "Register" << std::endl;
}


class B : public Interface
{
    public:
        B();
        void Echo(void *data);
};

B::B()
{
    Register( (Callback)Echo );
}

void B::Echo(void *data)
{
    std::cout << "Echo" << std::endl;
}


int main()
{
    B b;
    return 0;
}

这是我在 g++ 4.6.1 下遇到的错误:

test.cpp: In constructor ‘B::B()’:
test.cpp:31:22: error: argument of type ‘void (B::)(void*)’ does not match ‘Callback {aka void (Interface::*)(void*)}’

谁能解释我做错了什么?谢谢

4

2 回答 2

1

正如@Kerrek正确指出的那样,Echo不是 的成员Interface,因此B::Echo不符合Interface::*Callback. 但是您可以使用模板来完成此操作,例如:

template <class T> class Interface {
public:
    typedef void (T::*Callback)(void *data);
    void Register(Callback func) {
        std::cout << "Register" << std::endl;
    }
    // ...
};

class B : public Interface<B> {
public:
    B() {
        Register(&B::Echo);
    }
    void Echo(void *data) {
        // Do something
    }
};
于 2011-12-13T22:28:19.060 回答
0

我认为您最好使用 std::function (c++11) 或 boost::function (c++03+boost)

#include <iostream>

class Interface;
typedef void (Interface::*Callback)(void *data);

class Interface
{
    public:
        std::function<void(void*)> register;

            Interface(std::function<void(void*)> register_)
            :    register(register_)   //intializer list
            {}
            virtual ~Interface(){} //put me in
};

void Interface::Register(Callback func)
{
    std::cout << "Register" << std::endl;
}


class B : public Interface
{
    public:
        B();
        void Echo(void *data);
};

B::B()
:   Interface( std::bind(B::Echo, this) )
{}

void B::Echo(void *data)
{
    std::cout << "Echo" << std::endl;
}

虽然你为什么不使用纯虚拟是我无法理解的

class Interface
{
    public:
        virtual void Echo(void*)=0;

};

void B::Echo(void *data) //implements Echo
{
    std::cout << "Echo" << std::endl;
}

call interface->echo 将调用孩子

如果您需要性能,请使用

http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

并且要非常小心 void* 它们通常被认为是坏的。

编辑评论中的地址:非纯虚拟

class Interface
{
public:
    virtual ~Interface(){} //put me in
    virtual void echo(void*){}  //if implementation is not extended it will do nothing.
    //others 
};

这不是 Java,接口不是由语言定义的东西。这样,您可以拥有一个可以选择的接口,可以选择要实现的部分,如果回调与您的类无关,那么就不要实现它。

void* 不好,原因有很多。来自 C++ 常见问题

避免 void* (如果您真的需要它们,请将它们保留在低级函数和数据结构中,并向您的用户提供类型安全的接口,通常是模板)

http://www2.research.att.com/~bs/bs_faq.html

搜索“无效*”

但基本上 void* 绕过了 C++ 不遗余力地添加的所有类型安全。这是 C 中的一个 hack,以弥补它没有任何多态性或通用代码的事实。

于 2011-12-13T22:18:28.480 回答