2010-09-30 16 views
5

Una herramienta de verificación estática muestra una violación en el código de abajo:Sobre downcasting de clase base a la subclase puntero

class CSplitFrame : public CFrameWnd 
... 
class CVsApp : public CWinApp 
CWnd* CVsApp::GetSheetView(LPCSTR WindowText) 
{ 
CWnd* pWnd = reinterpret_cast<CSplitFrame*>(m_pMainWnd)->m_OutputBar.GetChildWnd(WindowText); 
return pWnd; 
} 

mensaje de error: clase hereda 'CSplitFrame' de la clase 'CWnd'

Descripción: Evite arrojar la jerarquía de herencia. Esta regla detecta lanzamientos desde un puntero de clase base a un puntero de subclase.

Beneficios: Permitir moldes en la jerarquía de herencia conduce a problemas de mantenimiento, y la bajada desde una clase base siempre es ilegal.

Referencias:

  1. Scott Meyers, "A partir de C++: 50 maneras específicas para mejorar sus programas y Diseño", segunda edición, Addison-Wesley, (C) 2005 Pearson Education, Inc., Capítulo : "La herencia y Diseño Orientado a objetos", artículo 39
  2. ataque conjunto luchador, Vehículo aéreo, C++ Normas de codificación Capítulo 4.23 conversiones de tipos, Regla AV 178

¿Crees que es una buena práctica para no bajar desde un puntero de clase base a un puntero de subclase? ¿Por qué y cuándo debo seguir esta regla?

+1

¿Está ejecutando un analizador estático en el código MFC? ¿Estás bromeando no? MFC rompe muchos estándares de codificación JSF, el código generado por el asistente rompe más, en realidad no está diseñado para la seguridad del tipo. –

+0

BTW: sería una buena idea dejar de llamar subclases de clases derivadas. La derivación/herencia es un concepto sintáctico, la subclasificación es un concepto de tipado, y las dos son ideas más o menos independientes. – Yttrill

Respuesta

4

Vayamos a través de algunos de los ejemplo downcasting en MFC:

CButton * de CWnd *

CWnd* wnd = GetDlgItem(IDC_BUTTON_ID); 
CButton* btn = dynamic_cast<CButton*>(wnd); 

CChildWnd * desde CFrameWnd *

CChildWnd * pChild = ((CSplitFrame*)(AfxGetApp()->m_pMainWnd))->GetActive(); 

De hecho, existen algunas de las limitación del diseño de MFC.

Debido a CWnd proporciona la funcionalidad básica de todas las clases de ventana en MFC, que ni siquiera sirven como una clase base de vista, de diálogo, etc. Botón

Si queremos evitar downcasting, probablemente necesitamos MFC pirata informático para dividir CWnd en menos piezas?

Ahora, llega a la otra pregunta, la forma de resolver la violación, mi humilde opinión es tratar de evitar downcasting insegura, mediante el uso de downcasting segura:

Parent *pParent = new Parent; 
Parent *pChild = new Child; 

Child *p1 = static_cast<Child*>(pParent); // Unsafe downcasting:it assigns the address of a base-class object (Parent) to a derived class (Child) pointer 
Parent *p2 = static_cast<Child*>(pChild); // Safe downcasting:it assigns the address of a derived-class object to a base-class pointer 

Se sirven como una buena práctica para el uso de downcasting seguro, aunque la violación todavía existe, simplemente suprimiremos la violación con la explicación dada.

Pocos de la referencia útil:
http://support.microsoft.com/kb/108587
http://blog.csdn.net/ecai/archive/2004/06/26/27458.aspx
http://www.codeproject.com/KB/mcpp/castingbasics.aspx
http://www.bogotobogo.com/cplusplus/upcasting_downcasting.html

Por último, gracias por la respuesta varios útiles de todos ustedes.
Son de hecho muy útiles.

10

reinterpret_cast es ciertamente una mala idea aquí, independientemente de los estándares de codificación o la teoría OOP. Tiene que ser dynamic_cast o boost::polymorphic_downcast.

En cuanto al capítulo 39 de Efectivo C++, se concentra en los problemas de mantenimiento ocasionados por tener que abatido a varios tipos diferentes y tener que comprobar los valores de retorno de dynamic_cast de posibles fallos, lo que resulta en múltiples ramas en el código:

Lo importante es esto: el estilo de programación if-then-else que el downcasting invariablemente conduce a es muy inferior al uso de funciones virtuales, y debe reservarlo para situaciones en las que realmente no tiene otra alternativa.

+4

O 'static_cast', si se conoce el tipo dinámico del objeto. –

+3

En realidad, lo que dije es engañoso. 'static_cast (p)' se puede usar con seguridad si se sabe que '* p' es una' T'. Para que ese sea el caso, 'T' no necesariamente tiene que ser el tipo dinámico de' * p'. 'T' también podría ser una clase base del tipo dinámico de' * p' y una clase derivada del tipo estático de '* p'. 'T' no necesariamente tiene que ser el tipo dinámico de' * p'. –

0

No me parece que en realidad es necesario que realice el reparto de todos modos, o al menos no como lo está haciendo. Su función debe devolver un CWnd, por lo que no necesita convertir a CSplitFrame.

Hubiera esperado que GetChildWnd devuelva CWnd* o similar; por qué no puede usted escribir algo como: indicadores de la clase

CWnd* CVsApp::GetSheetView(LPCSTR WindowText) 
{ 
    return m_pMainWnd->m_OutputBar.GetChildWnd(WindowText); 
} 
+1

m_pMainWnd es miembro de CWnd, que no tiene acceso a m_OutputBar, que es miembro de CSplitFrame. – wengseng

0

downcasting generalmente es algo que debe ser evitado a toda costa en programación orientada a objetos diseñado correctamente. Sin embargo, a veces tales conversiones son necesarias, especialmente si está utilizando alguna otra biblioteca/código que fue diseñado para requerir tales conversiones en el código del cliente. MFC es un ejemplo de dicha biblioteca.

Cuando los downcasts son realmente necesarios, nunca se deben realizar con reinterpret_cast.La forma correcta de realizar el reparto es dynamic_cast o static_cast.

Muy a menudo la gente usa reinterpret_cast cuando ven a una conversión de estilo C utilizado para abatido en el código y deciden convertirlo en C++ - Reparto estilo, suponiendo erróneamente que una conversión de estilo C en tal contexto es equivalente a reinterpret_cast. En realidad, el molde de estilo C aplicado al par de tipo padre-hijo en cualquier dirección es equivalente a static_cast con algunas ventajas adicionales (el estilo C puede romper la protección de acceso). Por lo tanto, una vez más, el uso de moldes de estilo C para este propósito es incorrecto, pero el reemplazo correcto de C++ no es reinterpret_cast, sino más bien dynamic_cast o static_cast.

Cuestiones relacionadas