TN016: 使用 mfc 的 C++ 多重繼承
這個註解告訴您,如何使用 Mfc 的多重繼承 (英哩)。 英哩的使用不需要使用 MFC。 英哩不使用於任何的 MFC 類別,並不需要撰寫類別庫。
下列子主題將描述英哩如何影響使用一般 MFC 的語言,所以,以及其中涵蓋部分英哩的限制。 這些限制包括一般 C++ 的限制。 其他人會受 MFC 架構。
在這份技術提示結尾處,您會發現使用英哩的完整 MFC 應用程式。
CRuntimeClass
持續性和動態物件建立機制,MFC 使用的 CRuntimeClass 來唯一辨認類別的資料結構。 MFC 會將這些結構的其中一個應用程式中的每個動態和/或可序列化類別關聯。 使用特殊的靜態物件型別的應用程式啟動時,會初始化這些結構AFX_CLASSINIT。
目前的實作CRuntimeClass不支援英哩的執行階段型別資訊。 但這不表示您不能使用英哩,MFC 應用程式中。 不過,您將必須特定責任,當您使用具有一個以上的基底類別的物件。
CObject::IsKindOf方法不會正確地判斷物件的型別如果有多個基底類別。 因此,您不能使用 CObject 做為虛擬的基底類別和所有呼叫CObject成員函式類似CObject::Serialize和新 CObject::operator必須具有範圍識別項,因此該 C++ 可以明確執行適當的函式呼叫。 當程式使用 MFC 內英哩時,該類別,包含CObject基底類別必須為基底類別清單中的最左邊的類別。
另一個方法是使用dynamic_cast運算子。 將具有英哩,其中一個基底類別的物件轉型會強制編譯器所提供的基底類別中使用之函式。 如需詳細資訊,請參閱 dynamic_cast 運算子。
CObject-根的所有類別
所有的重要類別直接或間接衍生自類別CObject。 CObject並沒有任何成員的資料,但是有一些預設功能。 當您使用英哩時,您通常會繼承自兩個或多個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 ?
實作 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 和執行階段輸入
The run-time typing mechanism supported by MFC in CObject uses the macros DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL and 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
使用英哩的範例程式
下列範例是獨立的應用程式所組成一個類別衍生自CFrameWnd和 CWinApp。 我們不建議您組織的應用程式,如此一來,但這是最小的 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;