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派生类通常将继承。 下面的示例演示类可以如何从 CFrameWndCObList继承:

class CListWnd : public CFrameWnd, public CObList
{
 ...
};
CListWnd myListWnd;

在这种情况下 CObject 包括的两次。 这意味着需要方式消除任何引用。 CObject 方法或运算符。 operator newdelete 运算符 是必须由包含的两个运算符。 另一个示例,下面的代码将产生错误在编译时:

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 虚方法不明确,因为它在 CFrameWndCObList不同的方式实现。 最好的方法取消多义性将按照上一节中存在的建议。

CObject::IsKindOf 和运行时类型

CObject 的 MFC 支持的运行时类型的结构使用宏 DECLARE_DYNAMICIMPLEMENT_DYNAMICDECLARE_DYNCREATEIMPLEMENT_DYNCREATEDECLARE_SERIALIMPLEMENT_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 的示例程序

下面的示例包括从 CFrameWndCWinApp派生的一类独立的应用程序。 建议不要这样 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;

请参见

其他资源

由Number "技术说明

技术说明按类别