TN016: Usando C++ múltiplo herança com MFC
Esta nota descreve como usar a herança múltipla (MI) com o Microsoft Foundation Classes.O uso de MI não é necessário com MFC.MI não é usado em quaisquer classes MFC e não é necessário escrever uma biblioteca de classe.
Os subtópicos a seguintes descrevem como MI afeta o uso comum obter MFC bem como cobrindo algumas restrições de MI.Algumas dessas restrições são as restrições gerais do C++.Outros são impostos pela arquitetura do MFC.
No final desta nota técnica, você encontrará um aplicativo MFC completo que usa MI.
CRuntimeClass
Persistência e mecanismos de criação do objeto dinâmico do uso do MFC a CRuntimeClass estrutura de dados para identificar exclusivamente a classes.MFC associa uma dessas estruturas com cada classe dinâmica e/ou serializável no 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 do CRuntimeClass não oferece suporte a informações de tipo de tempo de execução de MI.Isso não significa que você não pode usar MI em seu aplicativo do MFC.No entanto, você terá certas responsabilidades ao trabalhar com objetos que têm mais de uma classe base.
O CObject::IsKindOf método não corretamente determinará o tipo de um objeto se ele tiver várias classes base.Portanto, não é possível usar CObject como uma classe base virtual e todas as chamadas para CObject funções de membro como CObject::Serialize e CObject::operator novo deve ter qualificadores de escopo, de forma que C++ pode remover a ambigüidade de chamada de função apropriada.Quando um programa usa MI dentro do MFC, a classe que contém o CObject a classe base precisa ser a classe mais à esquerda da lista de classes base.
Uma alternativa é usar o dynamic_cast operador.Um objeto com MI a uma das suas classes base para forçar o compilador para usar as funções na classe base fornecida.Para mais informações, consulte Operador de dynamic_cast.
CObject - raiz de todas as Classes
Todas as classes significativas derivam direta ou indiretamente da classe CObject.CObjectoferece não tem dados membro, mas ele tem algumas funcionalidades padrão.Quando você usar MI, você normalmente herda de dois ou mais CObject-classes 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;
Nesse caso CObject é incluído duas vezes.Isso significa que você precisa de uma maneira a ambigüidade de qualquer referência a CObject métodos ou operadores.O operator new e operador excluir são dois operadores ambigüidade devem ser removidas.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 CObject métodos
Quando você cria uma nova classe que possui dois ou mais CObject derivadas de classes base, você deve reimplementar o CObject métodos que você deseja que outras pessoas usem.Operadores new e delete são obrigatórias e de despejo é recomendada.Os reimplements de exemplo a seguir o new e delete operadores 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 herdando praticamente CObject resolveria o problema da ambigüidade da função, mas não é o caso.Porque não há nenhum dado de membro em CObject, você não precisa de herança virtual para impedir que várias cópias dos dados de membro classe base.No primeiro exemplo foi mostrado anteriormente, o Dump método virtual é ainda ambíguo porque ele é implementado de forma diferente no CFrameWnd e CObList.A melhor maneira de remover a ambigüidade é siga as recomendações apresentadas na seção anterior.
CObject::IsKindOf e tempo de execução digitando
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.Essas macros podem executar uma verificação de tipo de tempo de execução para garantir a segurança downcasts.
Essas macros oferecem suporte a uma única classe de base e funcionarão de forma limitada para classes herdadas multiplicar.A classe base que você especificar no IMPLEMENT_DYNAMIC ou IMPLEMENT_SERIAL deve ser a classe base primeira (ou mais à esquerda).Esse posicionamento permitirá que você digitar verificando a classe base mais à esquerda somente.O sistema de tempo de execução será saber nada sobre classes de base adicionais.No exemplo a seguir, os sistemas de tempo de execução fará digite verificação contra CFrameWnd, mas será saber nada sobre CObList.
class CListWnd : public CFrameWnd, public CObList
{
DECLARE_DYNAMIC(CListWnd)
...
};
IMPLEMENT_DYNAMIC(CListWnd, CFrameWnd)
CWnd e mapas de mensagem
Para o sistema de mapa de mensagem do MFC funcione corretamente, há dois requisitos adicionais:
Deve haver apenas um CWnd-derivada da classe base.
O CWnd-derivado classe base deve ser a classe base primeira (ou mais à esquerda).
Aqui estão alguns exemplos não funcionará:
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ê estruturar um aplicativo dessa maneira, mas este é um exemplo de aplicativo MFC menor 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;