programacion - Clases de MFC y moldes de estilo C++
programacion visual c++ (3)
Me pregunto si no he entendido por completo los moldes de C ++ frente al antiguo molde de estilo C. En MFC tengo este método:
CWnd * GetDlgItem(UINT uResId);
Estoy esperando que un CComboBox (o CEdit), que se deriva de CWnd, requiera este tipo de elenco:
dynamic_cast<CComboBox *>(GetDlgItem(IDC_COMBO1));
// for CEdit:
dynamic_cast<CEdit *>(GetDlgItem(IDC_EDIT1));
pero esta operación causa un bloqueo al usar un puntero nulo, lo que significa que el lanzamiento ha fallado. Utilizando:
reinterpret_cast<CComboBox *>(GetDlgItem(IDC_COMBO1));
// for CEdit:
reinterpret_cast<CEdit *>(GetDlgItem(IDC_EDIT1));
soluciona el problema, pero estoy decepcionado. ¿Qué me estoy perdiendo?
Me pregunto si no he entendido por completo los moldes de C ++ frente al antiguo molde de estilo C. En MFC tengo este método:
Probablemente comprenda bien la diferencia, pero MFC se lanzó antes que RTTI en el estándar C ++, y tiene su propio soporte para RTTI , que no cumple con la norma .
Entonces, como alternativa, podría usar DYNAMIC_DOWNCAST
en DYNAMIC_DOWNCAST
lugar de la siguiente manera:
DYNAMIC_DOWNCAST(CEdit, GetDlgItem(IDC_EDIT1));
La práctica común para esto, sin embargo, no es lanzar, sino crear una variable miembro que represente su control MFC, utilizando DDX_Control , que puede lograr fácilmente haciendo clic derecho y seleccionando Agregar variable ... o mediante Clase MFC Wizzard .
EDITAR Entonces entendí mal una parte esencial de la pregunta del OP sobre cuándo se produce el bloqueo. El bloqueo se debe a la desreferencia de nullptr
, el resultado válido de dynamic_cast
, no el dynamic_cast
sí. @xMRi responde por qué falla en detalle.
El problema es que GetDlgItem
puede devolver un puntero CWnd*
temporal.
Si la ventana es una clase derivada de CWnd
y la ventana se crea con CWnd::Create(Ex)
o la ventana tiene una subclase, RTTI funcionará.
Cuando Windows crea la ventana (debido a una plantilla de diálogo) y la Ventana no está subclasificada por el MFC (con DDX_Control
ow CWnd::SubclassWindow
), GetDlgItem
simplemente devuelve un CWnd*
temporal, con CWnd::FromHandle
. Este identificador de ventana siempre es del tipo base CWnd
.
Si desea verificar si esta ventana es realmente un control de Edit
, puede usar CWnd::GetClassName
. La CEdit*
a un CEdit*
es segura y conveniente porque un control CEdit
comunica con su homólogo HWND
solo a través de los mensajes de la ventana. Entonces esto funciona para todas las clases básicas de ventanas integradas.
class A {
public:
A() {};
virtual ~A(){}
};
class B : public A {
public:
B() {};
virtual ~B() {}
};
int main()
{
A* a = new A();
B* b = dynamic_cast<B*>(a);
// b is 0
return 0;
}
Como en winocc.cpp
CWnd* CWnd::GetDlgItem(int nID) const
{
ASSERT(::IsWindow(m_hWnd));
if (m_pCtrlCont == NULL)
return CWnd::FromHandle(::GetDlgItem(m_hWnd, nID));
else
return m_pCtrlCont->GetDlgItem(nID);
}
y wincore.cpp
CWnd* PASCAL CWnd::FromHandle(HWND hWnd)
{
CHandleMap* pMap = afxMapHWND(TRUE); //create map if not exist
ASSERT(pMap != NULL);
CWnd* pWnd = (CWnd*)pMap->FromHandle(hWnd);
pWnd->AttachControlSite(pMap);
ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd);
return pWnd;
}
Cuando CHandleMap contiene el objeto es CWnd no un CComboBox o cualquier otro tipo de clase derivada, no funcionará utilizando dynamic_cast para rechazar.