テクニカル: MFC での C++ の多重継承の使用

この複数継承 (MI)、Microsoft Foundation Classes を使用する方法について説明します。

なぜ複数の継承?

MI の値を C++ やオブジェクト指向のコミュニティでの継続的な議論があります。Visual C コンパイラおよび開発環境が完全に MI をサポートします。

MFC クラス ライブラリは MFC を使用するを理解する必要がないように設計されています。MI 任意の MFC クラスでは使用されません。我々 は、MI はクラス ライブラリを作成する必要はありませんも深刻なアプリケーションの作成に必要なことを発見しました。私たちは決定を残すので MI を使用するには、個人的な意思決定することができます。

使用する MI にしたいです。?

MI を使用、パフォーマンスのトレードオフを理解し、MFC を使用する方法を既に理解して場合は、ここのために、あなたが何をする必要があります教えてくれます。制限のいくつかの一般的な C++ の制限、他、MFC アーキテクチャによって課されています。

どのように MI に影響する一般的な MFC イディオムの使用の技術的な問題のいくつかを以下に示します。MI を使用して、完全な MFC アプリケーションが含まれているこのテクニカル ノートの最後にコンパイルしすることができますを抽出。

CRuntimeClass

永続性と MFC のダイナミック オブジェクト作成機構CRuntimeClassデータ構造を使用してクラスを一意に識別します。MFC は各動的またはシリアル化可能なクラスのアプリケーションでこのタイプの 1 つの構造を関連付けます。これらの構造体は、起動時に AFX_CLASSINIT型の特別な静的オブジェクトを使用してアプリケーションの起動時に初期化されます。MFC の改版を変更する可能性があるので、自分でこの情報の実装を意識する必要がないです。

CRuntimeClassの現在の実装は、多重継承のランタイム型情報をサポートしていません。これは、MFC アプリケーションでは、MI を使用することはできませんが、その場合は、1 つ以上の基本クラスを持つオブジェクトを操作するとき特定の責任を必要するわけではありません。

複数の基本クラスがCObject::IsKindOfメンバー関数オブジェクトの型を正しく判断しません。したがって、 CObjectを仮想の基本クラスとして使用できません、すべての呼び出しをCObjectメンバー関数Serialize新しい演算子など、C++、適切な関数呼び出しを明確することができますのでスコープ修飾子が必要になります。MFC で MI を使用する必要がある場合は、[、 CObject基本クラス リストで最も左にあるクラスの基本クラスを含むクラスにする必要があります。

MI の乱用、使用上のアドバイスについて高度な C++ プログラミング スタイルとイディオムでジェームズ ・ o ・ Coplien (Addison Wesley, 1992年) を参照してください。

CObject - すべてのクラスのルート

あなたが知っているように、すべての重要なクラス直接または間接的クラスCObjectから派生します。CObjectはメンバー データはありませんが、いくつかの既定の機能が。MI を使用する場合、2 つ以上CObjectから継承する共通される-たとえば、 CFrameWnd CObListのクラスの派生:

cListWnd クラス: パブリック CFrameWnd、公開 CObList{...};CListWnd myListWnd

CObjectは含まれている 2 つの問題につながる 2 回:

推奨される手順

2 つ以上CObjectの新しいクラスを作成する基本クラスを派生すると、使用する人々 を期待これらCObjectメンバーを再実装します。演算子新しい削除必須され、ダンプお勧めします。たとえば:

cListWnd クラス: パブリック CFrameWnd、公開 CObList{パブリック。特価;void ※ 演算子新しい (size_t nSize){CFrameWnd::operator new(nSize) を返す;}void 演算子 delete (void ※ p){CFrameWnd::operator delete(&p);}CDumpContent (dc) のダンプを無効します。{CFrameWnd::Dump(dc);CObList::Dump(dc);}...}

CObject の仮想継承?

「CObject を仮想的に継承する場合は、あなたを求めることができるすべてのあいまいさの問題でしょうか。」。

効率的な Microsoft オブジェクト モデルでは、仮想継承は (ちょうど複数継承効率的単一継承である場合に) 非仮想継承として効率的ではないです。CObjectのメンバー データがないので、仮想継承には基本クラスのメンバー データの複数のコピーを防ぐため不要します。

本当の答えはない、仮想継承されます示したあいまいさの問題を解決することはできません。例: ( CFrameWndCObList異なる実装から)ダンプ仮想メンバー関数はまだあいまいです。

したがって、上記の手順に従って曖昧さ回避を提供するために勧め:

CObject::IsKindOf と実行時の入力

CObjectの MFC でサポートされているランタイムの入力機構マクロDECLARE_DYNAMIC IMPLEMENT_DYNAMIC、DECLARE_DYNCREATE IMPLEMENT_DYNCREATE DECLARE_SERIALIMPLEMENT_SERIALを使用します。これら安全キャスト ダウンする実行時の型チェックを行うことができます。

これらのマクロでは、のみ 1 つの基本クラスをサポート、多重継承クラスの限られた方法で動作します。IMPLEMENT_DYNAMICまたはIMPLEMENT_SERIALを指定する基本クラスは、最初 (最も左) の基本クラスをする必要があります。たとえば,

cListWnd クラス: パブリック CFrameWnd、公開 CObList{特価;DECLARE_DYNAMIC(CListWnd)...};IMPLEMENT_DYNAMIC (CListWnd、CFrameWnd)(&N)

これは、型は、左端の基本クラスのみをチェックするになります。実行時の型が追加拠点 (CObListこの場合) についての何も知っています。

CWnd とメッセージ マップ

MFC メッセージ マップ システムが正しく動作するために、ある 2 つの追加の要件:

上記の例では、 CFrameWndで最初の基本クラス。

いくつかの例は動作しません:

cTwoWindows クラス: パブリック CFrameWnd、パブリック CEdit特価;{ ... };//エラー: CWnd の 2 つのコピーCListEdit クラス: 公開 CObList、パブリック CEdit{ ... };//エラー: (CWnd から派生) CEdit 最初する必要があります(&N)

MI を使用して、サンプル プログラム

次の例は、 CFrameWndCWinAppから派生した 1 つのクラスで構成される、スタンドアロンのアプリケーションです。アプリケーションを構築するのには、この方法はお勧めではないがこれは、最小の MFC アプリケーションを 1 つのクラスの例。

次のプログラムをカットして、HELLOAPP の上をコピーできます。単一継承の Mfc サンプルの CPP HELLOAPP。通常どおり、プログラムを構築します。

#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;

番号順テクニカル ノート|nbsp;カテゴリ別テクニカル ノート(&N)

Index