TN016: Utilizando la herencia múltiple de C++ con MFC

Esta nota describe cómo utilizar herencia múltiple (MI) con Microsoft Foundation Classes.

¿Por qué herencia múltiple?

Hay un debate en curso en las comunidades orientada a objetos y C++ sobre el valor de MI. El entorno de desarrollo y compilador de Visual C++ apoya plenamente MI.

La biblioteca de clases MFC ha sido diseñada para que no necesita comprender MI utilizar MFC. MI no se utiliza en cualquiera de las clases MFC. Hemos encontrado que MI no es necesario escribir una biblioteca de clases, ni es necesario para escribir aplicaciones graves. Para utilizar MI o no puede ser una decisión personal, por lo dejamos esa decisión.

¿Desea utilizar MI?

Si ya sabe cómo utilizar MI, entender las ventajas y desventajas de rendimiento y desea utilizar MFC, esta nota le dirá lo que debe hacer. Algunas de las restricciones son restricciones generales de C++, otros son impuestas por la arquitectura MFC.

A continuación describen algunos de los aspectos técnicos de cómo MI afectar el uso de expresiones idiomáticas comunes de MFC. Al final de esta nota técnica se incluye una aplicación MFC completa utilizando MI que puede extraer y recopilar.

CRuntimeClass

La persistencia y los mecanismos de creación de objeto dinámico de MFC utilizan la estructura de datos CRuntimeClass para identificar las clases. MFC asocia cada clase serializable y dinámica en la aplicación de una estructura de este tipo. Estas estructuras se inicializan en tiempo de arranque de aplicaciones utilizando un objeto estático especial de tipo AFX_CLASSINIT. Necesita preocuparse no con la aplicación de esta información, ya que es probable que cambie entre revisiones de MFC.

La implementación actual de CRuntimeClass no admite información de tipo de tiempo de ejecución de herencia múltiple. Esto no significa no puede usar MI en una aplicación MFC, pero si lo hace, tendrá ciertas responsabilidades cuando se trabaja con objetos que tengan más de una clase base.

La función de miembro de CObject::IsKindOf no correctamente determinará el tipo de un objeto si tiene varias clases base. Por lo tanto, no puede utilizar CObject como una clase base virtual, y todas las llamadas a funciones de miembro de CObject como Serialize y nuevo operador necesitará tener calificadores de alcance C++ puede eliminar la ambigüedad de la llamada de función apropiada. Si encuentra la necesidad de utilizar MI dentro de MFC, entonces debería asegurarse de que la clase que contiene la clase base CObject la clase de la mayoría de la izquierda en la lista de clases base.

Para obtener información sobre los usos y abusos de MI, ver estilos de programación de C++ avanzada y modismos por James O. Coplien (Addison Wesley, 1992).

CObject - la raíz de todas las clases

Como ustedes saben, todas las clases importantes derivan directa o indirectamente de la clase CObject. CObject no tiene ningún dato miembro, pero tiene alguna funcionalidad predeterminada. Cuando se utiliza MI, será común para heredar de dos o más CObject-derivado de las clases, por ejemplo, un CFrameWnd y un CObList:

clase CListWnd: CFrameWnd pública, CObList pública
{
 ...
};
CListWnd myListWnd

En este caso CObject se incluye dos veces, lo que lleva a dos problemas:

Pasos recomendados

Al crear una nueva clase con dos o más CObject deriva las clases base, implementar a los miembros de CObject que esperar que la gente utilice. Los operadores nuevos y Eliminar son obligatorios, volcado es recomendable. Por ejemplo:

clase CListWnd: CFrameWnd pública, CObList pública
{
público:
 nbsp;  void * nuevo operador (size_t nSize)
        {return CFrameWnd::operator new(nSize);}
    operador void eliminar (void * p)
        {CFrameWnd::operator delete(p);}

void Dump (CDumpContent & dc)
        {CFrameWnd::Dump(dc);
          CObList::Dump(dc); }
     ...
}

¿Herencia virtual de CObject?

Usted puede preguntar, "si se hereda prácticamente CObject, no todos los problemas de ambigüedad desaparecen?".

Incluso en el modelo de objetos de Microsoft eficiente, herencia virtual no es tan eficiente como herencia no virtual (sólo como herencia múltiple no es tan eficiente como única herencia en algunos casos). Ya no hay miembros datos en CObject, herencia virtual no es necesario para evitar múltiples copias de datos de miembros de la clase base.

La verdadera respuesta es no, herencia virtual no resolverá los problemas de ambigüedad ilustrados por encima. Por ejemplo: la función de miembro virtual de volcado es todavía ambigua (ya que CFrameWnd y CObList aplicación diferente).

Por lo tanto, recomendamos siguiendo los pasos anteriores para proporcionar desambiguación:

CObject::IsKindOf y escribiendo en tiempo de ejecución

El mecanismo de escritura de ejecución apoyado por MFC en CObject utiliza las macros DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL y IMPLEMENT_SERIAL. Estos ofrecen la posibilidad de hacer una comprobación de tipos en tiempo de ejecución para permitir bajadas de reparto seguros.

Estas macros sólo admiten una sola clase base y trabajarán en forma limitada para clases multiplicar heredadas. La clase base que se puede especificar en IMPLEMENT_DYNAMIC o IMPLEMENT_SERIAL debe ser la clase base primera (o la mayoría de la izquierda). Por ejemplo,

clase CListWnd: CFrameWnd pública, CObList pública
{
 nbsp;  DECLARE_DY&NAMIC(CListWnd)
    ...
};
IMPLEMENT_DYNAMIC (CListWnd, CFrameWnd)

Esto le permitirá al tipo de comprobación de la clase base de la mayoría de la izquierda sólo. El sistema de tipo de tiempo de ejecución sabrá nada sobre bases adicionales (CObList , en este caso).

CWnd y mapas de mensajes

Para que el sistema de mapa de mensajes MFC funcione correctamente, existen dos requisitos adicionales:

En el ejemplo anterior, CFrameWnd es la clase de primera base.

Algunos ejemplos que no funcionarán:

clase CTwoWi&ndows: CFrameWnd pública, CEdit pública
 nbsp;  { ... };
        / / error: dos copias de CWnd

clase CListEdit: CObList pública, CEdit pública
    { ... };
        / / error: CEdit (derivado de CWnd) debe ser el primero

Un programa de ejemplo utilizando MI

En el siguiente ejemplo es una aplicación independiente que consiste en una clase derivada de CFrameWnd y CWinApp. Esta forma de estructurar una aplicación no es un recomendado, pero este es un ejemplo de la aplicación MFC más pequeño con una clase.

Puede cortar el siguiente programa y copiarlo en la parte superior de HELLOAPP.CPP en la muestra de MFC General única herencia HELLOAPP. A continuación, generar el programa como lo haría normalmente.

#include <afxwin.h>

class CHelloAppAndFrame : public CFrameWnd, public CWinApp
{ 
public:
    CHelloAppAndFrame()
        { }

    // Necessary evil for 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()

// since 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;

&Notas técnicas por número |nbsp; Notas técnicas por categoría

Index