C++ 在C+中处理层次结构中成员函数的指针+;
我试图对以下情况进行编码: 我有一个基类,它提供了一个处理事件的框架。我试图使用一个指向成员函数的指针数组来实现这一点。具体如下:C++ 在C+中处理层次结构中成员函数的指针+;,c++,inheritance,member-function-pointers,C++,Inheritance,Member Function Pointers,我试图对以下情况进行编码: 我有一个基类,它提供了一个处理事件的框架。我试图使用一个指向成员函数的指针数组来实现这一点。具体如下: 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 regi
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\u t无法转换为EH::func\u t。这对我来说很有意义。在我的例子中,转换是安全的,因为下的对象实际上是DEH。所以我想要这样的东西:
void EH::DEH_handle_event_5_wrapper() {
DEH *p = dynamic_cast<DEH *>(this);
assert(p != NULL);
p->handle_event_5();
}
在DEH::DEH()中
放
所以,最后一个问题(花了我足够长的时间…):
有没有一种方法可以自动创建这些包装(比如EH::DEH\u handle\u event\u 5\u包装
)呢?
或者做类似的事情?
对于这种情况还有什么其他的解决方案
谢谢。你真的不应该这样做。检查boost::bind
精化:
首先,我敦促你重新考虑你的设计。我见过的大多数事件处理程序系统都涉及一个外部注册器对象,它维护事件到处理程序对象的映射。您在EventHandler类中嵌入了注册,并且正在基于函数指针进行映射,这是不太理想的。您遇到了问题,因为您正在围绕内置虚拟函数行为进行结束运行
boost::bind
等的要点是使用函数指针创建对象,从而允许您利用面向对象的语言特性。因此,以您的设计为起点,基于boost::bind
的实现将如下所示:
struct EventCallback
{
virtual ~EventCallback() { }
virtual void handleEvent() = 0;
};
template <class FuncObj>
struct EventCallbackFuncObj : public IEventCallback
{
EventCallbackT(FuncObj funcObj) :
m_funcObj(funcObj) { }
virtual ~EventCallbackT() { }
virtual void handleEvent()
{
m_funcObj();
}
private:
FuncObj m_funcObj;
};
void register_handler(int event_num, EventCallback* pCallback)
{
m_callbacks[event_num] = pCallback;
}
您的注册电话如下所示:
register_handler(event,
new EventCallbackFuncObj(boost::bind(&DEH::DEH_handle_event_5_wrapper, this)));
现在,您可以从任何类型的(对象、成员函数)创建回调对象,并将其保存为给定事件的事件处理程序,而无需编写自定义函数包装器对象。为什么不使用虚拟函数?差不多
class EH {
public:
void handle_event(int event_num) {
// Do any pre-processing...
// Invoke subclass hook
subclass_handle_event( event_num );
// Do any post-processing...
}
private:
virtual void subclass_handle_event( int event_num ) {}
};
class DEH : public EH {
public:
DEH() { }
private:
virtual void subclass_handle_event( int event_num ) {
if ( event_num == 5 ) {
// ...
}
}
};
您可以简单地使用static\u cast
将DEH::func\u t
转换为EH::func\u t
,而不是为所有派生类中的每个处理程序创建一个包装器(当然,这远不是一种可行的方法)。成员指针是反变的:它们自然地向下转换层次结构,并且可以使用static\u cast
手动向上转换层次结构(与普通对象指针相反,它们是协变的)
您正在处理的情况正是扩展静态\u cast
功能以允许成员指针向上转换的原因。此外,成员函数指针的非平凡内部结构也以这种方式实现,以正确处理此类情况
所以,你可以简单地做
DEH() {
func_t f5 = &DEH::handle_event_5;
register_handler(5, static_cast<EH::func_t>(f5));
........
}
为了使它看起来更优雅,您可以在DEH
中为register\u handler
提供包装,或者使用其他方法(宏?模板?)隐藏强制转换
此方法不提供在调用时验证处理程序指针有效性的任何方法(在基于包装器的版本中,可以使用dynamic\u cast
)。我不知道你有多在乎这张支票。我想说的是,在这种情况下,这实际上是不必要和过度的。请您详细说明一下好吗?我有一种感觉,应该有一种方法可以把所有的装订材料都做得更好,但是我想不出来,这是可能的。然而,我想让DEH的实现对用户来说尽可能简单——在所有事件中,“如果”都会非常难看。@anatoli:那么你认为创建派生类的用户会发现指向成员的指针比“如果”或“开关”构造更简单吗?很抱歉,我从网上掉下来了——帐户有问题。我同意你的看法,指向议员的指示是非常恶劣的。然而,我认为,在这种情况下,这些东西大部分是在引擎盖下。用户注册处理程序的方法非常简单,并且遵循一个非常简单的模板。同样,与if/switch解决方案相反,在这种情况下,基类可以做更多的检查——例如,所有事件都有处理程序等等。它解决了这个问题。我知道强制转换是可能的,但根据标准,它有一个未定义的行为。@anatoli:不,行为是定义的,假设调用是有效的。同样,调用的有效性在调用时确定,也就是说,如果对象具有正确的类型并且指针指向正确的方法,那么一切都正常。标准中就是这样定义的。
class EH {
public:
void handle_event(int event_num) {
// Do any pre-processing...
// Invoke subclass hook
subclass_handle_event( event_num );
// Do any post-processing...
}
private:
virtual void subclass_handle_event( int event_num ) {}
};
class DEH : public EH {
public:
DEH() { }
private:
virtual void subclass_handle_event( int event_num ) {
if ( event_num == 5 ) {
// ...
}
}
};
DEH() {
func_t f5 = &DEH::handle_event_5;
register_handler(5, static_cast<EH::func_t>(f5));
........
}
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);
........
}