如何在c++中定义一个接受派生类成员函数指针作为参数的类方法
How I do define a class method that accepts a derived class member function pointer as a parameter in C++?
我在这里使用Signal-Slot c++实现:https://github.com/pbhogan/Signals在我正在开发的用户界面库中实现事件侦听器。
面向用户的API如下:
class DialEventListener : public EventListener {
void handleEvent(Event event){
…
cout << "Event on" << event.source.name << end;
}
}
int main(){
Interactor dial1;
dial1.addListener(ROTATE_EVENT, DialEventListener());
…
}
要定义事件侦听器,用户将继承EventListener
以创建自定义事件处理程序。但是在Interactor::addListener()
的实现中,我使用Signals.h代码,你必须提供一个成员函数指针以及一个类实例指针来连接一个委托到一些信号。
下面是一个代码片段,展示了我如何尝试使用Signals来实现事件侦听器。我使用从EventType
s到Signal
的映射来说明触发多种类型事件的交互器。
typedef map< EventType, Signal1<Event> > EventTypeListenerMap;
class Interactor {
private:
EventTypeListenerMap listener_map;
public:
void TriggerEvent(Event e){
Signal1<Event> signal = listener_map[e.type];
signal(e);
}
void addListener(EventType e_type, EventListener listener){
…
//Find the signal for a particular kind of event.
Signal1<TUIKitEvent> signal = listener_map[e_type];
//Plug a delegate into that signal's slot
signal.Connect( &listener, &EventListener::handleEvent);
}
void addListener(EventType e_type, EventListener listener, void (EventListener::*handler)(Event)){
…
Signal1<TUIKitEvent> signal = listener_map[e_type];
signal.Connect( &listener, handler);
}
}
class EventListener {
virtual void handleEvent(Event event){
}
}
我使用信号槽实现的事实应该对用户隐藏,所以我只是写了这个版本的Interactor::addListener(EventType, EventListener, void (EventListener::*))
作为一个完整性检查,以确保我传递了正确的成员函数指针绑定到那个信号。但是成员函数指针是特定于类的,所以如何定义一个带有签名的函数,该签名接受指向从EventListener派生的类的方法的指针,这些方法可能还不存在?
有两种回调方法,要么提供一个具有将被调用的虚函数的接口,要么执行类型擦除以使其完全泛型。在第二个版本中,你可以使用预先打包的类型擦除解决方案,或者你需要使用一些模板来自己实现它。
您在第一个块中显示的预期用户代码遵循第一种方法,并且足以满足基本需求。但请注意,您可能希望在传递值的几个地方使用指针或引用:
// user code
Interactor dial;
DialEventListener dial_listener;
dial.addListener( ROTATE_EVENT, dial_listener );
// implementation
void Interactor::addListener( EventType event, EventListener & listener ) {
Signal1<TUIKitEvent> & sgn = listener_map[event];
sgn.Connect( &listener, &EventListener::handleEvent );
};
注意几个变化:listener
参数是通过引用而不是值传递的。如果你按值传递,你会得到切片(只有EventListener
子对象会被复制,它不会是addListener
中的DialEventListener
对象)。通过传递对基类型的引用,您可以在对象保持其标识的同时将其用作基。
这反过来要求您传入的对象不是临时的(您不能使用DialEventListener()
作为addListener
的第二个参数,因为临时的生命周期将在完整表达式的末尾结束,这意味着您将有一个悬空引用(在您当前的实现中,您在信号实现中有一个悬空指针,一旦信号被触发,这很可能会导致UB)。
另外,在addListener
内部,你不希望复制存储在映射中的信号,而是修改它,所以,当访问映射中的信号时,你应该使用引用而不是值。
你的类的文档应该非常明确,传入的对象的生命周期必须超过Interactor
(或者允许注销,以便客户端可以在回调被销毁之前从Interactor
中删除处理程序(再次,如果你不这样做,你将以UB结束)
另一种方法是执行类型擦除,我从未使用过特定的信号库,所以我在这里随机应变。您正在使用的信号库(可能)在内部实现了类型擦除,但是您需要能够转发确切的类型,直到库可以执行擦除,否则在接口中使用不同的类型擦除库。
// Transferring the exact type to the signal lib:
class Interactor {
// ...
public:
template <typename Listener>
void addListener( EventType event, Listener & listener, void (Listener::*callback)(Event) ) {
Signal1<TUIKitEvent> & sgn = listener_map[event];
sgn.connect( &listener, callback );
}
//...
在接口中使用不同的类型擦除库可能是可能的,也可能是不可能的,这取决于信号实现中connect
方法的语义。如果它复制了第一个参数,那么您可能可以使用std::function
:
class Interactor {
//...
public:
void addListener( EventType event, std::function< void (Event) > callback ) {
Signal1<TUIKitEvent> & sgn = listener_map[event];
sgn.connect( callback, &std::function<void(Event)>::operator() );
}
//...
};
假设库将复制第一个参数,并在内部维护它自己的副本(即假设它的行为类似于boost::signal
库)。在用户端,他们必须执行类型擦除:
struct AnotherListener { // no need to explicitly derive from EventListner now!
void method( Event e ) {}
};
int main() {
Interactor dial;
AnotherEventListener listener;
dial.addListener( dial, std::bind( &AnotherListener::method, &listener ) );
}
bind
的确切语法可能不是这样,我从来没有在boost库的新标准中使用过它,它应该是boost::bind( &AnotherListener::method, &listener, _1 );
…
- 如何使用指针传递给函数的数组中对象的函数成员
- c++构造函数成员初始化:传递参数
- 创建 std::函数,它返回具有函数成员值的变量.分段错误
- 如何在C++通过公共函数访问私有函数成员?
- 解释了构造函数成员初始化列表
- 调用std::函数成员时内存损坏
- 是否可以为模板类的模板函数成员设置别名?
- 捕获 lambda 函数C++成员变量
- 构造函数成员初始值设定项跨成员列出,安全吗?
- 获取与在模板参数中传递的函数成员类型相同的类
- 如何从公共函数成员访问地图私有成员
- C 构造函数成员分配优化
- 使用命名空间进行函数成员定义
- 函数成员作为 CUDA 内核的参数
- 模板基类函数成员的别名
- 函数成员中用于void和继承的enable_if
- 头文件中是否定义了一个很长的Class函数成员
- 类内/构造函数成员初始化
- 使用指向部分专用函数成员的指针自动填充向量
- 指向函数成员的指针