我正在尝试编写以下情况的代码:
我有一个基类,提供了处理事件的框架。我正在尝试为此使用指针到成员函数的数组。它如下:
class EH { // EventHandler
virtual void something(); // just to make sure we get RTTI
public:
typedef void (EH::*func_t)();
protected:
func_t funcs_d[10];
protected:
void register_handler(int event_num, func_t f) {
funcs_d[event_num] = f;
}
public:
void handle_event(int event_num) {
(this->*(funcs_d[event_num]))();
}
};
然后,应该假定用户从这一类派生其他类并提供处理程序:
class DEH : public EH {
public:
typedef void (DEH::*func_t)();
void handle_event_5();
DEH() {
func_t f5 = &DEH::handle_event_5;
register_handler(5, f5); // doesn't compile
........
}
};
由于无法将DEH::func_t转换为EH::func_t,因此无法编译该代码。这对我来说很有意义。在我的情况下,转换是安全的,因为
this
下的对象实际上是DEH。所以我想要这样的东西:void EH::DEH_handle_event_5_wrapper() {
DEH *p = dynamic_cast<DEH *>(this);
assert(p != NULL);
p->handle_event_5();
}
然后代替
func_t f5 = &DEH::handle_event_5;
register_handler(5, f5); // doesn't compile
在DEH::DEH()中
放
register_handler(5, &EH::DEH_handle_event_5_wrapper);
所以,最后一个问题(花了我足够长的时间……):
有没有一种方法可以自动创建这些包装器(例如
EH::DEH_handle_event_5_wrapper
)?还是要做类似的事情?
对于这种情况还有什么其他解决方案?
谢谢。
最佳答案
不必为所有派生类中的每个处理程序创建包装器(当然,甚至不是远程可行的方法),您只需使用static_cast
将DEH::func_t
转换为EH::func_t
即可。成员指针是互变的:它们自然地向下转换到层次结构,并且可以使用static_cast
(与普通对象指针相反,它们是协变的)手动向上转换到层次结构。
您正在处理的情况正是static_cast
功能得以扩展以允许成员指针向上转换的原因。而且,成员函数指针的非平凡的内部结构也以这种方式实现,以正确地处理这种情况。
因此,您只需
DEH() {
func_t f5 = &DEH::handle_event_5;
register_handler(5, static_cast<EH::func_t>(f5));
........
}
我要说的是,在这种情况下,没有必要定义typedef名称
DEH::func_t
-这毫无用处。如果删除DEH::func_t
的定义,则典型的注册代码如下所示DEH() {
func_t f5 = static_cast<func_t>(&DEH::handle_event_5);
// ... where `func_t` is the inherited `EH::func_t`
register_handler(5, f5);
........
}
为了使外观更加美观,您可以在
register_handler
中提供DEH
的包装,也可以使用其他方式(宏?模板)来隐藏 Actor 表。该方法没有为您提供任何方法在调用时验证处理程序指针的有效性(就像您可以在基于包装的版本中使用
dynamic_cast
一样)。我不知道您到底有多想在这里进行检查。我想说,在这种情况下,这实际上是不必要和过度的。