TN016:使用MFC的C++多重继承
此说明介绍如何将与 (MI) Microsoft 基础类 (mfc) 的多重继承。 使用 MI 无需与 MFC。 MI 不使用 MFC 类和不需要编写类库。
以下子主题描述 MI 如何影响使用常见的 MFC 惯例以及包括某些 MI 的限制。 其中的一些限制是一般 C++ 限制。 MFC 体系结构施加其他。
此技术说明结束时将会发现使用 MI 的完整 MFC 应用程序。
CRuntimeClass
MFC 持久性和动态对象创建结构使用 CRuntimeClass 数据结构唯一标识类。 MFC 关联这些机制之一可在应用程序中每个动态和/或可序列化的类。 ,当应用程序启动使用类型 AFX_CLASSINIT时,特定静态对象这些结构初始化。
CRuntimeClass 的当前实现不支持 MI 运行时类型信息。 这并不意味着您的 MFC 应用程序无法使用 MI。 但是,您将具有特定职责,当您具有多个基类的对象一起使用。
CObject::IsKindOf 方法不会正确确定对象的类型是否有多个基类。 因此,不能使用 CObject 作为虚拟基类,并且,所有对 CObject 成员函数例如 CObject::Serialize ,并 新CObject::operator 必须具有一个范围限定符,以便 C++ 可以消除适当的函数调用。 当程序使用在 MFC 中的 MI,包含 CObject 基类的类需要在基类列表中最左侧的类。
种方法是使用 dynamic_cast 运算符。 将带 MI 的对象到其基类之一在提供源类将强制编译器将使用函数。 有关更多信息,请参见 dynamic_cast运算符。
CObject - 所有类根
所有重要的类从类 CObject直接或间接派生。 CObject 没有任何成员数据,但是,它确实默认功能。 当您使用 MI,则从两个或多个 CObject派生类通常将继承。 下面的示例演示类可以如何从 CFrameWnd 和 CObList继承:
class CListWnd : public CFrameWnd, public CObList
{
...
};
CListWnd myListWnd;
在这种情况下 CObject 包括的两次。 这意味着需要方式消除任何引用。 CObject 方法或运算符。 operator new 和 delete 运算符 是必须由包含的两个运算符。 另一个示例,下面的代码将产生错误在编译时:
myListWnd.Dump(afxDump);
// compile time error, CFrameWnd::Dump or CObList::Dump ?
Reimplementing CObject 方法
当您创建具有两个或多个 CObject 派生的基类的新类时,应重新实现 CObject 方法希望其他人使用。 运算符 new 和 delete 是必须的,并建议 转储 。 下面的示例 reimplements new 和 delete 运算符和 Dump 方法:
class CListWnd : public CFrameWnd, public CObList
{
public:
void* operator new(size_t nSize)
{ return CFrameWnd::operator new(nSize); }
void operator delete(void* p)
{ CFrameWnd::operator delete(p); }
void Dump(CDumpContent& dc)
{ CFrameWnd::Dump(dc);
CObList::Dump(dc); }
...
};
CObject 虚拟继承
似乎虚拟继承 CObject 将解析多义性功能的,因此,但事实并非如此。 由于没有在 CObject的成员数据,您不必虚拟继承防止基类成员数据的多个副本。 在显示之前的第一个示例中, Dump 虚方法不明确,因为它在 CFrameWnd 和 CObList不同的方式实现。 最好的方法取消多义性将按照上一节中存在的建议。
CObject::IsKindOf 和运行时类型
在 CObject 的 MFC 支持的运行时类型的结构使用宏 DECLARE_DYNAMIC、 IMPLEMENT_DYNAMIC、 DECLARE_DYNCREATE、 IMPLEMENT_DYNCREATE、 DECLARE_SERIAL 和 IMPLEMENT_SERIAL。 这些宏可以执行一个运行时类型检查确保安全的向下转换。
这些宏只支持单一基类,并方式与受限方式使用个多重继承的类。 在 IMPLEMENT_DYNAMIC 指定的基类或 IMPLEMENT_SERIAL 应是第一个 (或最左侧) 基类。 此位置可以执行只检查最左侧的基类的类型。 该运行时类型任何系统不知道附加的基类。 在下面的示例中,运行时系统不会执行任何操作检查 CFrameWnd的类型,但请注意,有关 CObList。
class CListWnd : public CFrameWnd, public CObList
{
DECLARE_DYNAMIC(CListWnd)
...
};
IMPLEMENT_DYNAMIC(CListWnd, CFrameWnd)
CWnd 和消息映射
为了使正常工作 MFC 消息映射的系统,有两个附加要求:
必须只有一 CWnd派生的基类。
CWnd派生的基类必须是第一个 (或最左侧) 基类。
这是不起作用的一些示例:
class CTwoWindows : public CFrameWnd, public CEdit
{ ... };
// error : two copies of CWnd
class CListEdit : public CObList, public CEdit
{ ... };
// error : CEdit (derived from CWnd) must be first
使用 MI 的示例程序
下面的示例包括从 CFrameWnd 和 CWinApp派生的一类独立的应用程序。 建议不要这样 framework 应用程序,不过,这是一个类最小的 MFC 应用程序的示例。
#include <afxwin.h>
class CHelloAppAndFrame : public CFrameWnd, public CWinApp
{
public:
CHelloAppAndFrame()
{ }
// Necessary because of 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()
// because 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;