Sdílet prostřednictvím


TN016: Použití vícenásobné dědičnosti jazyka C++ v prostředí MFC

Tato poznámka popisuje použití vícenásobné dědičnosti (MI) s Microsoft Foundation Classes.Použití MI není vyžadován s knihovnou MFC.MI se nepoužívá ve všech tříd knihovny MFC a není povinen napsat knihovnu tříd.

Následující dílčí témata popisují, jak MI ovlivňuje využívání společné MFC idioms stejně jako které se vztahuje některá omezení MI.Některé z těchto omezení jsou omezení obecného jazyka C++.Ostatní jsou uložené MFC architekturu.

Na konci této technické poznámce najdete kompletní aplikaci knihovny MFC, která používá MI.

CRuntimeClass

Perzistence a mechanismy vytváření dynamických objektů knihovny MFC použití CRuntimeClass datovou strukturu pro jednoznačnou identifikaci třídy.MFC jeden z těchto struktur přidruží každý dynamický nebo serializovatelné třídy ve vaší aplikaci.Tyto struktury jsou inicializovány při spuštění aplikace pomocí speciální statický objekt typu AFX_CLASSINIT.

Aktuální provádění CRuntimeClass MI informace o typu runtime nepodporuje.To však neznamená, že MI nelze použít v aplikaci knihovny MFC.Při práci s objekty, které mají více než jeden základní třídy, však bude mít určité povinnosti.

CObject::IsKindOf Metoda nesmí nesprávně určí typ objektu, pokud má více základních tříd.Proto nelze použít třídy CObject jako virtuální základní třídy a všechna volání CObject členské funkce, jako CObject::Serialize a Nový CObject::operator musí mít Kvalifikátory oboru tak, že C++ lze rozlišit volání příslušnou funkci.Pokud program používá MI v rámci knihovny MFC, třídy, která obsahuje CObject základní třída musí být třída nejvíce vlevo v seznamu základních tříd.

Další možností je použít dynamic_cast operátor.Obsazení objektu se MI na jeden z jeho základních tříd způsobí, že kompilátor v základní třídy zadané funkce.Další informace naleznete v tématu dynamic_cast – operátor.

Třídy CObject - kořen všech tříd

Všechny významné třídy jsou odvozeny přímo nebo nepřímo z třídy CObject.CObjectneobsahuje žádná data člen, ale má některé výchozí funkce.Při použití MI je obvykle zdědí od dvou nebo více CObject-odvozené třídy.Následující příklad ukazuje, jak lze zdědit třídu CFrameWnd a CObList:

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

V tomto případě CObject je součástí dvakrát.To znamená, že potřebujete způsob, jak odstranit všechny odkazy na CObject metodami nebo operátorů.operator new a operátor delete jsou dva operátory, které musí být jednoznačně rozlišit.Například následující kód způsobí chybu v čase kompilace:

myListWnd.Dump(afxDump);
    // compile time error, CFrameWnd::Dump or CObList::Dump ?

Metody reimplementing třídy CObject

Při vytvoření nové třídy, který má dva nebo více CObject základní třídy odvozené měli přeimplementovat CObject metod, které chcete ostatním uživatelům.Operátory new a delete jsou povinné a Výpis je doporučeno.Následující příklad reimplements new a delete subjekty a Dump metoda:

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); }
     ...
};

Virtuální dědičnost třídy CObject

To může zdát, že prakticky dědění CObject by vyřešit problém funkce nejednoznačnosti, ale to není tento případ.Protože nejsou žádná data člen v CObject, není třeba virtuální dědičnost zabránit více kopií dat člen základní třídy.V prvním příkladu, uvedený výše Dump virtuální metoda je stále nejednoznačný, protože je implementována odlišně v CFrameWnd a CObList.Nejlepší způsob, jak odebrat nejednoznačnosti je dodržovat doporučení uvedené v předchozí části.

CObject::IsKindOf a zadáním příkazu Run-Time

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.Tato makra můžete provést k zajištění bezpečné downcasts kontrola typu v době spuštění.

Tato makra podporují pouze jeden základní třídy a bude fungovat v omezené míře násobit zděděných tříd.Základní třídy v IMPLEMENT_DYNAMIC nebo IMPLEMENT_SERIAL by měla být základní třídy první (nebo nejvíce vlevo).Toto umístění vám umožní kontrolu základní třídy nejvíce vlevo pouze typu.Typu v době spuštění systému bude vědět nic o další základní třídy.V následujícím příkladu běhu systémů provede kontrolu proti typu CFrameWnd, ale nic vědět o CObList.

class CListWnd : public CFrameWnd, public CObList
{
    DECLARE_DYNAMIC(CListWnd)
    ...
};
IMPLEMENT_DYNAMIC(CListWnd, CFrameWnd)

CWnd mapování a zprávy

Systém mapu zpráv MFC pracovat správně existují dva další požadavky:

  • Musí být pouze jeden CWnd-základní třídy odvozené.

  • CWnd-Odvozené třídy základní musí být základní třída první (nebo nejvíce vlevo).

Zde je několik příkladů, které nefungují:

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

Ukázka programu pomocí MI

Následující ukázka je samostatná aplikace, která se skládá z jedné třídy odvozené z CFrameWnd a CWinApp.Nedoporučujeme struktury aplikace tímto způsobem, že toto je příklad nejmenší aplikace knihovny MFC, která obsahuje jednu třídu.

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

Viz také

Další zdroje

Technické poznámky podle čísel

Technické poznámky podle kategorií