テクニカル ノート 16: MFC における C++ の多重継承
ここでは、 Microsoft の基本クラスで複数の継承 (MI) を使用する方法について説明します。MI の使用は MFC で必要ではありません。MI は、 MFC クラスでは使用されず、クラス ライブラリを記述する必要はありません。
次のサブトピックは MI がどのように、 MFC のコモンの方法の使用に影響を与えたり、 MI の制限をカバーする方法について説明します。これらの制限は一般 C++ の制限です。そのほかの MFC アーキテクチャによって課されます。
このテクニカル ノートの末尾に MI を使用する完全な MFC アプリケーションを検索します。
CRuntimeClass
MFC の永続性、および動的オブジェクトの作成機能は、クラスを識別するために CRuntimeClass のデータ構造を使用します。MFC では、アプリケーションでの動的やシリアル化可能なクラスとこれらの構造の 1 種類を関連付けます。これらの構造はアプリケーションが特別な静的オブジェクトの型 AFX_CLASSINITを使用して起動時に初期化されます。
CRuntimeClass の現在の実装は MI の実行時の型情報をサポートしません。これにより、 MFC アプリケーションで MI を使用できないわけではありません。ただし、複数の基本クラスがあるオブジェクトを使用すると、特定の責任があります。
CObject::IsKindOf のメソッドが正しく複数の基本クラスがある場合、オブジェクト型を確認しません。したがって、仮想基本クラスとして CObject を使用できず C++ が適切な関数呼び出しを区別できるように CObject::Serialize などの CObject のメンバー関数と CObject::operator new へのすべての呼び出しはスコープの修飾子が必要です。プログラムが MFC 内の MI を使用すると、 CObject 基本クラスを含むクラスは、基本クラスのリストの左端のクラスである必要があります。
また、 dynamic_cast 演算子を使用することです。基本クラスの 1 つがへの MI のオブジェクトをキャスト コンパイラは、指定された基本クラスの関数を使用します。詳細については、「dynamic_cast 演算子」を参照してください。
CObject - すべてのクラスのルート
すべての重要なクラスは、クラス CObjectから直接的または間接的に派生します。CObject にメンバーのデータがない場合は、既定の機能があります。MI を使用すると、複数の CObject派生クラスからは継承します。クラスが CFrameWnd と CObListから継承する方法を次の例に示します。:
class CListWnd : public CFrameWnd, public CObList
{
...
};
CListWnd myListWnd;
この場合 CObject は、包含 2 になります。これは CObject のメソッドまたは演算子への参照を区別する方法が必要なことを意味します。operator new と operator delete は区別する必要がある演算子は次の 2 つです。別の例として、次のコードは、コンパイル時にエラーが発生します:
myListWnd.Dump(afxDump);
// compile time error, CFrameWnd::Dump or CObList::Dump ?
Reimplementing CObject のメソッド
複数の CObject に、から派生した基本クラスの新しいクラスを作成すると、他のユーザーに使用する再 CObject のメソッド。new と delete 演算子は必須で、 ダンプ をお勧めします。次の例の混在 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にメンバーのデータがないため、仮想継承が基本クラスのメンバーのデータの複数のコピーを防ぐ必要はありません。前述した最初の例では CFrameWnd と CObList間で実行されるため、 Dump の仮想メソッドは、あいまいです。あいまいさを削除する最良の方法は、前のセクションで説明した推奨事項に従うことです。
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 メッセージ マップのシステム用に 2 ビットの追加要件があります:
1 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から派生したクラス 1 から構成されるスタンドアロン アプリケーションです。ただし、アプリケーションをこのように構成しますが、これは 1 個のクラスがある最小の 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;