Partilhar via


TN016: Usando herança múltipla de C++ com MFC

Esta nota descreve como usar herança múltipla (MI) com as Classes de Fundação da Microsoft. O uso de MI não é necessário com MFC. MI não é usado em nenhuma classe MFC e não é necessário para escrever uma biblioteca de classes.

Os subtópicos a seguir descrevem como o MI afeta o uso de expressões idiomáticas comuns do MFC e cobrem algumas das restrições do MI. Algumas dessas restrições são restrições gerais de C++. Outros são impostos pela arquitetura MFC.

No final desta nota técnica, você encontrará um aplicativo MFC completo que usa MI.

CRuntimeClass

Os mecanismos de persistência e criação dinâmica de objetos do MFC usam a estrutura de dados CRuntimeClass para identificar classes exclusivamente. MFC associa uma dessas estruturas a cada classe dinâmica e/ou serializável em seu aplicativo. Essas estruturas são inicializadas quando o aplicativo é iniciado usando um objeto estático especial do tipo AFX_CLASSINIT.

A implementação atual de CRuntimeClass não suporta informações de tipo de tempo de execução MI. Isso não significa que você não pode usar MI em seu aplicativo MFC. No entanto, você terá certas responsabilidades quando trabalhar com objetos que tenham mais de uma classe base.

O método CObject::IsKindOf não determinará corretamente o tipo de um objeto se ele tiver várias classes base. Portanto, você não pode usar CObject como uma classe base virtual, e todas as chamadas para CObject funções de membro, como CObject::Serialize e CObject::operator new devem ter qualificadores de escopo para que C++ possa desambiguar a chamada de função apropriada. Quando um programa usa MI dentro do MFC, a classe que contém a CObject classe base precisa ser a classe mais à esquerda na lista de classes base.

Uma alternativa é usar o dynamic_cast operador. Transmitir um objeto com MI para uma de suas classes base forçará o compilador a usar as funções na classe base fornecida. Para obter mais informações, consulte operador dynamic_cast.

CObject - A raiz de todas as classes

Todas as classes significativas derivam direta ou indiretamente da classe CObject. CObject não tem dados de membro, mas tem alguma funcionalidade padrão. Quando você usa MI, normalmente herda de duas ou mais CObjectclasses derivadas. O exemplo a seguir ilustra como uma classe pode herdar de um CFrameWnd e um CObList:

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

Neste caso CObject é incluído duas vezes. Isso significa que você precisa de uma maneira de desambiguar qualquer referência a CObject métodos ou operadores. O operador novo e o operador delete são dois operadores que devem ser desambiguados. Como outro exemplo, o código a seguir causa um erro em tempo de compilação:

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

Reimplementação de métodos de CObject

Quando você cria uma nova classe que tem duas ou mais CObject classes base derivadas, você deve reimplementar os CObject métodos que você deseja que outras pessoas usem. Operadores new e delete são obrigatórios e Dump é recomendado. O exemplo a seguir reimplementa os new operadores e delete e o Dump método:

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

Herança virtual de CObject

Pode parecer que herdar virtualmente CObject resolveria o problema da ambiguidade funcional, mas não é esse o caso. Como não há dados de membro no CObject, você não precisa de herança virtual para impedir várias cópias de dados de membros de uma classe base. No primeiro exemplo que foi mostrado anteriormente, o método virtual ainda é ambíguo Dump porque é implementado de forma diferente em CFrameWnd e CObList. A melhor maneira de remover a ambiguidade é seguir as recomendações apresentadas na seção anterior.

CObject::IsKindOf e Run-Time Digitação

O mecanismo de definição de tipos em tempo de execução suportado pelo MFC em CObject utiliza as macros DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL e IMPLEMENT_SERIAL. Essas macros podem executar uma verificação de tipo de tempo de execução para garantir downcasts seguros.

Essas macros suportam apenas uma única classe base e funcionarão de forma limitada para multiplicar classes herdadas. A classe base especificada em IMPLEMENT_DYNAMIC ou IMPLEMENT_SERIAL deve ser a primeira (ou a mais à esquerda) classe base. Esse posicionamento permitirá que você faça a verificação de tipo apenas para a classe base mais à esquerda. O sistema de tipo de tempo de execução não saberá nada sobre classes base adicionais. No exemplo a seguir, os sistemas de tempo de execução farão a verificação de tipo em relação ao CFrameWnd, mas não saberão nada sobre CObList.

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

CWnd e Mapas de Mensagens

Para que o sistema de mapa de mensagens MFC funcione corretamente, há dois requisitos adicionais:

  • Deve haver apenas uma CWndclasse base derivada.

  • A classe base derivada de CWnd deve ser a primeira (ou a mais à esquerda) das classes base.

Aqui estão alguns exemplos que não funcionarão:

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

Um programa de exemplo usando MI

O exemplo a seguir é um aplicativo autônomo que consiste em uma classe derivada de CFrameWnd e CWinApp. Não recomendamos que você estruture um aplicativo dessa maneira, mas este é um exemplo do menor aplicativo MFC que tem uma 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;

Ver também

Notas técnicas por número
Notas técnicas por categoria