Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En esta nota se describe cómo usar la herencia múltiple (MI) con las Microsoft Foundation Classes. El uso de MI no es necesario con MFC. MI no se usa en ninguna clase MFC y no es necesario para crear una biblioteca de clases.
En los subtemas siguientes, se describe cómo afecta la herencia múltiple al uso de expresiones comunes de MFC, así como describir algunas de las restricciones de la herencia múltiple. Algunas de estas restricciones son restricciones generales de C++. Otras son impuestas por la arquitectura MFC.
Al final de esta nota técnica encontrará una aplicación MFC completa que usa MI.
CRuntimeClass
Los mecanismos de creación de objetos dinámicos y persistencia de MFC usan la estructura de datos CRuntimeClass para identificar de forma única las clases. MFC asocia una de estas estructuras a cada clase dinámica o serializable en la aplicación. Estas estructuras se inicializan cuando la aplicación se inicia mediante un objeto estático especial de tipo AFX_CLASSINIT
.
La implementación actual de CRuntimeClass
no admite la información de tipo herencia múltiple en tiempo de ejecución. Esto no significa que no pueda usar MI en la aplicación MFC. Sin embargo, tendrá ciertas responsabilidades cuando trabaje con objetos que tengan más de una clase base.
El método CObject::IsKindOf no determinará correctamente el tipo de un objeto si tiene varias clases base. Por lo tanto, no puede usar CObject como una clase base virtual y todas las llamadas a CObject
funciones miembro como CObject::Serialize y CObject::operator new deben tener calificadores de ámbito para que C++ pueda desambiguar la llamada de función adecuada. Cuando un programa usa MI dentro de MFC, la clase que contiene la CObject
clase base debe ser la clase más a la izquierda en la lista de clases base.
Una alternativa es usar el dynamic_cast
operador . La conversión de un objeto con MI a una de sus clases base obligará al compilador a usar las funciones de la clase base proporcionada. Para obtener más información, consulte el operador dynamic_cast.
CObject: la raíz de todas las clases
Todas las clases significativas derivan directa o indirectamente de la clase CObject
.
CObject
no tiene ningún dato de miembro, pero tiene alguna funcionalidad predeterminada. Por lo general, cuando se usa la herencia múltiple, se heredará de dos o más clases derivadas de CObject
. En el ejemplo siguiente se muestra cómo una clase puede heredar de un CFrameWnd y un CObList:
class CListWnd : public CFrameWnd, public CObList
{
// ...
};
CListWnd myListWnd;
En este caso CObject
se incluye dos veces. Esto significa que necesita una manera de desambiguar cualquier referencia a CObject
métodos o operadores. El operador new y operator delete son dos operadores que se deben desambiguar. Como otro ejemplo, el código siguiente produce un error en tiempo de compilación:
myListWnd.Dump(afxDump); // compile time error, CFrameWnd::Dump or CObList::Dump
Reimplementación de métodos CObject
Al crear una nueva clase que tenga dos o más CObject
clases base derivadas, debe volver a implementar los CObject
métodos que desea que usen otras personas. Los operadores new
y delete
son obligatorios y Dump, recomendado. En el ejemplo siguiente se vuelven a implementar los new
operadores y delete
y el 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);
}
// ...
};
Herencia virtual de CObject
Podría parecer que la herencia CObject
virtual solucionaría el problema de ambigüedad de funciones, pero eso no es así. Dado que no hay datos de miembro en CObject
, no necesita herencia virtual para evitar varias copias de datos de miembro de clase base. En el primer ejemplo que se mostró anteriormente, el Dump
método virtual sigue siendo ambiguo porque se implementa de forma diferente en CFrameWnd
y CObList
. La mejor manera de quitar la ambigüedad es seguir las recomendaciones presentadas en la sección anterior.
CObject::IsKindOf y Run-Time Typing
El mecanismo de escritura en tiempo de ejecución compatible con MFC en CObject
usa las macros DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL y IMPLEMENT_SERIAL. Estas macros puede realizar una comprobación de tipos en tiempo de ejecución para garantizar la seguridad de las conversiones a tipos heredados.
Estas macros solo admiten una sola clase base y funcionarán de forma limitada para multiplicar las clases heredadas. La clase base que especifique en IMPLEMENT_DYNAMIC o IMPLEMENT_SERIAL debe ser la primera clase base (o más izquierda). Esta colocación le permitirá realizar la comprobación de tipos solo para la clase base que está más a la izquierda. El sistema de tipos en tiempo de ejecución no sabrá nada sobre las clases base adicionales. En el ejemplo siguiente, los sistemas en tiempo de ejecución realizarán la comprobación de tipos en CFrameWnd
, pero no sabrán nada sobre CObList
.
class CListWnd : public CFrameWnd, public CObList
{
DECLARE_DYNAMIC(CListWnd)
// ...
};
IMPLEMENT_DYNAMIC(CListWnd, CFrameWnd)
CWnd y asignaciones de mensajes
Para que el sistema de asignación de mensajes MFC funcione correctamente, hay dos requisitos adicionales:
Solo debe haber una
CWnd
clase base derivada.La clase base derivada de
CWnd
debe ser la primera clase base (o la que se encuentra más a la izquierda).
Estos son algunos ejemplos que no funcionarán:
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 programa de ejemplo mediante MI
El ejemplo siguiente es una aplicación independiente que consta de una clase derivada de CFrameWnd
y CWinApp. No se recomienda estructurar una aplicación de esta manera, pero se trata de un ejemplo de la aplicación MFC más pequeña que tiene una clase.
#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;