GetDlgItem为什么要进行强制类型转换

(CListBox*)GetDlgItem(IDC_LIST1)->AddString("abc");
这是为list box控件增加内容的一种常见写法,习以为常了,一直没注意到这里有什么古怪。有次手痒,把它改成了

dynamic_cast<CListBox*>(GetDlgItem(IDC_LIST1))->AddString("abc");

发现crash了,原因是dynamic_cast<CListBox*>(GetDlgItem(IDC_LIST1)返回值为空。简单搜索了一下,看到很多人有这个困惑,但并没有在网上找到现在的有说服力的解释,就自己查了一下MFC这部分代码。发现这行代码执行的原理大致是这样的:

CWnd::GetDlgItem会在一个Handle Map里查找,如果有对应的对象就返回,如果没有就新建一个CWnd的对象返回它的指针。指针指向的对象就是一个CWnd对象,根本就不存在CListBox对象,所以dynamic_cast<CListBox*>(GetDlgItem(IDC_LIST1))返回值为空就很正常了。

但另一个疑问就出现了:既然CListBox对象不存在,那强制转换成CListBox*类型并调用它的函数为什么能执行呢?

这就得了解类中对象与函数的关系了。面向对象的说法给人一种幻觉,好像一个对象就是一个全功能的物体,数据和函数都在这个对象里,如果对象不存在,那函数也就不存在了。其实对象只包括数据,类的函数是共用的,如果有虚函数的话,有个虚表指针,如果没有虚函数的话,对象里就只有数据了。调用类的成员函数实际上是,把对象的地址作为参数来调用成员函数而已。函数只管检查参数类型是否正确,并不管对象是否真实存在。把AddString的定义写成下面这样就容易理解了:
int AddString(CListBox* pListBox, LPCTSTR lpszItem);

而这个函数能否执行成功,就看函数里是否使用了子类的数据成员,如果只使用了父类的数据成员,那么是可以工作的。再看一下AddString函数的实现:

_AFXWIN_INLINE int CListBox::AddString(LPCTSTR lpszItem)
{ 
    ASSERT(::IsWindow(m_hWnd)); 
    return (int)::SendMessage(m_hWnd, LB_ADDSTRING, 0, (LPARAM)lpszItem); 
}

发现只调用了m_hWnd,这是父类的一个成员变量,所以是可以工作的。MFC里大量用SendMessage来编写成员函数,而不是直接调用对应的函数,这种通用性应该也是考虑的因素之一了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值