以 MFC使用C++的多继承下来我们来看看如何在 MFC中使用多继承(MI)。用一个东西总要知道为什么要用它。MI在C++和面向对象编程界可是很有争议,MI可以新类从多个父类中继承一些属性。虽然存在争议,VC++的编译器和开发环境完全支持MI。MFC类库设计得使用户不用理解什么是MI就可以使用了。我们也发现不需要使用MI编写类库,使用它编写某些应用程序也是可用可不用的,那么用与不同就看用户的喜好了。如果你决定使用 MI,那就看看如何使用它吧,下面有一些规定是基于C++的要求的,有一些是MFC专门要求的。我们有必要看一下 CruntimeClass,MFC的永久和动态对象创建机器都使用它的数据结构来唯一标记一个类。在应用程序启动时,使用AFX_CLASSINIT的静态对象初始化一个应用程序。对于它的实现,用户不用操心。在VC++ 5中不支持MI运行(runtime)类型信息,但这并不意味着用户不能在MFC应用程序中使用MI。CObject::IsKindOf 函数对于使用MI的类来说就不能正确识别它的类型了,原因很简单,它父类有多个,这个函数无法确定了。因此用户不能使用Cobject作为虚基类,所有对Cobject成员函数的调用都需要知道它的作用域,而且在调用的时候要特别小心。如果用户确实需要在MFC中使用MI,一定要保证在使用MI时,如果使用到了Cobject,一定要把它放在继承类的最左边。下来让我们看看 Cobject,这个所有类的老祖宗。所有的类都直接或间接继承来自Cobject,它没有数据成员,但有一些成员函数。在使用MI时,这些特定的成员函数会从Cobject的不同派生类中继承过来。class CListWnd : public CFrameWnd, public CObList { 上面的情况中, Cobject被包括了两次,这导致了两个问题:
myListWnd.Dump(afxDump); //这里会有一个编译错误,编译器不明白你调用的究竟是哪一个类的Dump,是CFrameWnd::Dump还是CObList::Dump? 有了问题我们就要解决,让我们看看如何解决: 在上面的情况中,你必须重新实现那些有二义性的函数,让我们来看个例子: class CListWnd : public CFrameWnd, public CObList void operator delete(void* p) { CFrameWnd::operator delete(p); } //调用的是CframeWnd中的delete void Dump(CDumpContent& dc) { CFrameWnd::Dump(dc); CObList::Dump(dc); } 如果我们使用虚继承这些二义性会随之消失吗?不是这样。因为CObject中没有数据成员,所以也没有必要防止产生多个重复的基类数据成员复本。虚继承不能消除二义性,我们可以回想一下Dump函数,它仍然是二义的,因为CFrameWnd和CObject对它的实现是不同的。让我们看一下下面的步骤,它可以帮助你消除这种二义性: 运行 (runtime)类型机制支持一些宏:DECLARE_DYNAMIC,IMPLEMENT_DYNAMIC,DECLARE_DYNCREATE,IMPLEMENT_DYNCREATE,DECLARE_SERIAL和IMPLEMENT_SERIAL,这些宏在单继承的时候工作得不错,可是对于多继承,出现在IMPLEMENT_DYNAMIC或IMPLEMENT_SERIAL中的必须是在继承关系最左边的基类。class CListWnd : public CFrameWnd, public CObList 对于消息映射可又有些麻烦了,它要求下面两项要求:
下面一些例子是错误的: class CTwoWindows : public CFrameWnd, public
CEdit class CListEdit : public CObList, public CEdit 下面我们看一下微软公司提供的类子,没事不要使用这种结构。 #include <afxwin.h> class CHelloAppAndFrame : public CFrameWnd, public CWinApp
{
public:
CHelloAppAndFrame()
{ }
// Necessary evil for MI disambiguity
void* operator new(size_t nSize)
{ return CFrameWnd::operator new(nSize); }
void operator delete(void* p)
{ CFrameWnd::operator delete(p); }
// Implementation
// CWinApp overrides
virtual BOOL InitInstance();
// CFrameWnd overrides
virtual void PostNcDestroy();
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP() }; BEGIN_MESSAGE_MAP(CHelloAppAndFrame, CFrameWnd)
ON_WM_PAINT()
END_MESSAGE_MAP()
// since the frame window is not allocated on the heap, we must
// override PostNCDestroy not to delete the frame object
void CHelloAppAndFrame::PostNcDestroy()
{
// do nothing (do not call base class)
}
void CHelloAppAndFrame::OnPaint()
{
CPaintDC dc(this);
CRect rect;
GetClientRect(rect);
CString s = "Hello, Windows!";
dc.SetTextAlign(TA_BASELINE | TA_CENTER);
dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
dc.SetBkMode(TRANSPARENT);
dc.TextOut(rect.right / 2, rect.bottom / 2, s);
}
// Application initialization
BOOL CHelloAppAndFrame::InitInstance()
{
// first create the main frame
if (!CFrameWnd::Create(NULL, "Multiple Inheritance Sample",
WS_OVERLAPPEDWINDOW, rectDefault))
return FALSE;
// the application object is also a frame window
m_pMainWnd = this;
ShowWindow(m_nCmdShow);
return TRUE;
}
CHelloAppAndFrame theHelloAppAndFrame; |