我想知道我是否还没有完全理解C ++演员与旧C-Style演员。在MFC中我有这个方法:
CWnd * GetDlgItem(UINT uResId);
我期待来自CWnd的derived的CComboBox(或CEdit)需要这种演员:
dynamic_cast<CComboBox *>(GetDlgItem(IDC_COMBO1));
// for CEdit:
dynamic_cast<CEdit *>(GetDlgItem(IDC_EDIT1));
但是此操作会导致使用空指针崩溃,这意味着强制转换失败。使用:
reinterpret_cast<CComboBox *>(GetDlgItem(IDC_COMBO1));
// for CEdit:
reinterpret_cast<CEdit *>(GetDlgItem(IDC_EDIT1));
解决了这个问题,但我很失望。 我错过了什么?
答案 0 :(得分:4)
问题是GetDlgItem
可能会返回一个临时的CWnd*
指针。
如果窗口是CWnd
派生类,并且窗口是使用CWnd::Create(Ex)
创建的,或者窗口是子类,则RTTI将起作用。
当窗口由Windows创建时(由于对话框模板)并且窗口没有被MFC子类化(DDX_Control
ow CWnd::SubclassWindow
),GetDlgItem
只是使用CWnd*
返回临时CWnd::FromHandle
。此窗口句柄始终为基本类型CWnd
。
如果您想检查此窗口是否真的是Edit
控件,可以使用CWnd::GetClassName
。转换为CEdit*
是安全和方便的,因为CEdit
控件只通过Window消息与其HWND
对应方进行通信。所以这适用于所有基本的集成窗口类。
答案 1 :(得分:3)
我想知道我是否还没有完全理解C ++演员与旧C-Style演员。在MFC中我有这个方法:
可能你确实很好地理解了差异,但MFC已经在C ++标准的RTTI之前发布了its own support for RTTI,它不符合标准方式。
因此,您可以使用DYNAMIC_DOWNCAST
代替,如下所示:
DYNAMIC_DOWNCAST(CEdit, GetDlgItem(IDC_EDIT1));
然而,通常的做法不是强制转换,而是使用DDX_Control来创建代表MFC控件的成员变量,您可以通过右键单击,然后选择添加变量... 或通过MFC Class Wizard。
修改强>
所以我误解了OP关于崩溃何时发生的问题的一个重要部分。崩溃的原因是解除引用nullptr
, dynamic_cast
的有效结果,而不是dynamic_cast
本身。
@xMRi解答为何崩溃的原因。
答案 2 :(得分:2)
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;
}
与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);
}
和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;
}
当CHandleMap包含的对象是CWnd而不是CComboBox或任何其他派生类类型时,使用dynamic_cast进行强制转换就不行了。