Partager via


TN016 : À l'aide de l'héritage multiple C++ avec MFC

Cette remarque décrit comment utiliser l'héritage multiple (MI) avec Microsoft Foundation Classes.L'utilisation du mid n'est pas requise avec MFC.Le mid n'est utilisé dans une classe MFC et n'est pas nécessairement écrire une bibliothèque de classes.

Des sous-domaines suivantes décrivent comment le mid affecte l'utilisation de idiomes communs MFC ainsi que décrit certaines restrictions du mid.Quelques-unes de ces restrictions sont des restrictions générales C++.D'autres sont appliqués par l'architecture MFC.

À la fin de cette note technique vous trouverez une application complète MFC utilisant le mid.

CRuntimeClass

Les mécanismes de création d'objets de persistance et dynamique MFC utilisent la structure de données de CRuntimeClass pour identifier des classes.MFC associe une de ces structures avec chaque classe dynamique et/ou serializable dans votre application.Ces structures sont initialisées au démarrage de l'application à l'aide d'un objet statique spécial de type AFX_CLASSINIT.

L'implémentation actuelle d' CRuntimeClass ne prend pas en charge les informations de type au moment de l'exécution de mid.Cela ne signifie pas que vous ne pouvez pas utiliser le mid dans votre application MFC.Toutefois, vous aurez certaines responsabilités lorsque vous travaillez avec des objets qui ont plusieurs classes de base.

La méthode de CObject::IsKindOf ne détermine pas correctement le type d'un objet si elle a plusieurs classes de base.Par conséquent, vous ne pouvez pas utiliser CObject comme classe de base virtuelle, et tous les appels aux fonctions membres d' CObject telles que CObject::Serialize et au CObject::operator nouveau doivent avoir des qualificateurs de portée afin que C++ puisse supprimer l'ambiguïté de l'appel de fonction approprié.Lorsqu'un programme utilise le mid dans MFC, la classe qui contient la classe de base d' CObject doit être la classe de gauche dans la liste de classes de base.

Une autre solution consiste à utiliser l'opérateur d' dynamic_cast .Cast d'un objet avec le mid à l'une de ses classes de base oblige le compilateur utilise les fonctions dans la classe de base fournie.Pour plus d'informations, consultez opérateur de dynamic_cast.

CObject - la racine de toutes les classes

Toutes les classes significatives dérivent directement ou indirectement de la classe CObject.CObject n'a pas de membres de données, mais il possède certaines fonctionnalités par défaut.Lorsque vous utilisez le mid, vous héritent généralement de deux CObjectou plusieurs classes dérivées.l'exemple suivant montre comment une classe peut hériter de CFrameWnd et de CObList:

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

Dans ce cas CObject est inclus deux fois.Cela signifie que vous avez besoin de lever l'ambiguïté toute référence aux méthodes ou les opérateurs d' CObject .operator new et operator delete sont deux opérateurs qui doivent être désambiguïsés.Comme autre exemple, le code suivant provoque une erreur de compilation :

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

Méthodes de Reimplementing CObject

Lorsque vous créez une classe qui a deux classes de base dérivées ou plus d' CObject , vous devez réimplémenter les méthodes d' CObject que vous souhaitez d'autres personnes à utiliser.Les opérateurs new et delete sont obligatoires et dump est recommandée.Les dans l'exemple suivant les opérateurs d' new et d' delete et la méthode d' 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); }
     ...
};

Héritage virtuel de CObject

Il semble que héritage virtuel à partir CObject pourraient résoudre le problème de l'ambiguïté de fonction, mais ce n'est pas le cas.Étant donné qu'il n'y a pas de membres de données CObject, vous n'avez pas besoin d'héritage virtuel pour empêcher plusieurs copies des données d'un membre de la classe de base.Dans le premier exemple illustré précédemment, la méthode virtuelle d' Dump est encore ambiguë car il est implémenté différemment dans CFrameWnd et CObList.Le meilleur moyen de pour supprimer l'ambiguïté doit respecter les recommandations présentées dans la section précédente.

CObject::IsKindOf et taper à l'exécution

Le mécanisme tapant à l'exécution pris en charge par les MFC dans CObject utilise les macros DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL et IMPLEMENT_SERIAL.Ces macros peuvent effectuer une vérification de type au moment de l'exécution pour vérifier que les casts aval sécurisés.

La prise en charge de ces macros qu'une seule classe de base unique et exécuté de façon limitée pour multiplient des classes héritées.La classe de base que vous spécifiez dans IMPLEMENT_DYNAMIC ou IMPLEMENT_SERIAL doit être la première (ou de gauche) classe de base.Ce positionnement vous permet de faire de vérification de type de la classe de base de gauche uniquement.Le système de type d'exécution ne connaîtra rien sur les classes de base supplémentaires.Dans l'exemple suivant, les systèmes d'exécution est défini par la vérification de type par rapport à CFrameWnd, mais ne connaîtront rien à propos de CObList.

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

CWnd et les tables des messages

Pour que le système de table des messages MFC fonctionne correctement, il existe deux spécifications supplémentaires :

  • Il doit y avoir qu'un CWnd- classe de base dérivée.

  • CWnd- la classe de base dérivée doit être la première (ou de gauche) classe de base.

Voici quelques exemples qui ne fonctionnera pas :

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

Un exemple de programme à l'aide de le mid

l'exemple suivant est une application autonome qui se compose d'une classe dérivée d' CFrameWnd et de CWinApp.Nous vous structurez une application de cette manière, il constitue un exemple de la plus petite application MFC comportant une classe.

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

Voir aussi

Autres ressources

Notes techniques de nombres

Notes techniques de catégorie