MFC的派生类多根继承时,为什么必须将MFC类作为第一父类

来源:互联网 发布:薇诺娜淘宝上是正品吗 编辑:程序博客网 时间:2024/06/15 07:31
今天想起一个问题,C++中,如果要实现接口的话,必须使用多重继承机制。MFC中,假如我们有一个类,既需要从MFC窗口类派生,又需要实现我们程序内部的一个接口,例如下面的代码。
  1. class MyBaseClass
  2. {
  3. public:
  4.    virtual ~MyBaseClass(){} 
  5. }
  6. class MyDialog : public MyBaseClass, public CDialog
  7. {
  8.    ...
  9. }
如果在这类里添加消息映射函数的话,例如,添加OnLButtonDown消息函数
  1. class MyBaseClass
  2. {
  3. public:
  4.    virtual ~MyBaseClass(){} 
  5. }

  6. class MyDialog : public MyBaseClass, public CDialog
  7. {
  8. public:
  9.     DECLARE_MESSAGE_MAP()
  10.     afx_msg void OnLButtonDown(UINT flag, CPoint point);
  11. }

  1. //.c文件

  2. BEGIN_MESSAGE_MAP(MyDialog, CDialog)
  3.    ON_WM_LBUTTONDOWN()
  4. END_MESSAGE_MAP()

  5. void MyDialog::OnLButtonDown(UINT flag, CPoint point)
  6. {
  7. }
当点击点击MyDialog激活该消息时会出现异常。
出现这个异常的原因很简单,但是调查过程还是比较难的。很显然这个异常的原因是出现在消息映射部分。
我们看看ON_WM_LBUTTONDOWN()这个宏的定义:
  1. #define ON_WM_LBUTTONDOWN() /
  2.     { WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, /
  3.         (AFX_PMSG)(AFX_PMSGW) /
  4.         (static_cast< void (AFX_MSG_CALL CWnd::*)(UINT, CPoint) > ( &ThisClass :: OnLButtonDown)) },
可以看到,MyDialog::OnLButtonDown的函数地址被强制转化为void (AFX_MSG_CALL CWnd::*)(UINT, CPoint)函数类型了。
这样一说就能够完全明白了,对象成员函数的指针类型在使用时必须要2个参数:变量地址和成员函数的地址。MFC函数在调用该函数的时候是这样调用的:

  1. (this->*mmf.pfn_v_u_p)(static_cast<UINT>(wParam), point);
其中
mmf.pfn_v_u_p就是MyDialog::OnLButtonDown的地址,而对象的地址(MyDialog对象的地址)被直接当成CWnd的地址了。所以这里要求MFC类必须作为第一个父类。因为当成第二父类的时候,从对象地址到CWnd地址要差一个虚函数表指针的大小,这个虚函数表是第一父类的需函数表(当然,第一父类至少要包含一个virtual函数,不相信这个问题的朋友可以试试在第一父类一个虚函数也不声明,这个时侯就没有这个问题了)。这应该是MFC实现时的疏忽吧,因为可以简单的在DECLARE_MESSAGE_MAP()宏里添加一个虚函数即可:
  1. #define DECLARE_MESSAGE_MAP() /
  2.    MFC_DECLARE_MESSAGE_MAP(); /
  3.    virtual GetMFCObject(){return (CObject*)this;}
在调用消息映射函数时改为:

  1. (this->GetMFCObject()->*mmf.pfn_v_u_p)(static_cast<UINT>(wParam), point);
就应该可以了。



原创粉丝点击