UWP开发细节记录:WRL::ComPtr 的坑
2016-03-05 23:28
351 查看
WRL::ComPtr 取原始指针的地址有两种方式:
operator&() 先释放原指针再取地址
GetAddressOf() 直接得到原始指针的地址
显然,operator& 是为COM风格的API设计的,比如下面这种:
在这一点上和 ATL::CComPtr 一致,实现上则有不同。上面这种 API 最容易出现的错误是传了一个非空指针的地址给 ppObj ,然后指针被覆盖导致原来指向的对象无法释放。ATL::CComPtr 的解决办法是在 operator& 的实现中加了一个断言,如果指针非空就弹出警告;WRL::ComPtr 的解决办法如上所述,先释放再取地址。
从使用的角度 WRL::ComPtr 更方便,但从设计角度看实际上违背了运算符重载的基本原则。在不看文档的情况下没人能预期到 operator& 会修改原指针的值,这就是坑。
如下面这样一个 API:
双重指针参数实际代表的是一个指针数组,而非类似 CreateObject 中的指针地址。在数组长度为 1 时,习惯上直接取指针地址设为 ppObjs 参数,然后将 count 设为1 ,如下:
注意第六行的代码,无论 ptr 是裸指针还是ATL::CComPtr 包装的智能指针都正确的,不过 CComPtr 会引发断言失败使得我们不得不修改成其他实现方式;WRL::ComPtr 智能指针则编译运行一切正常结果却是悲剧。
以后使用 WRL::ComPtr 只能小心些了,只在 out 语义的参数中使用 operator& 其他一律用 GetAddressOf ,或者全部用 GetAddressOf 然后在用作 out 语义的参数前写断言。
设计上的小聪明还是要不得,哪怕是微软。
MSDN 中关于 ComPtr::operator& 运算符的备注说明 :
This method differs from ComPtr::GetAddressOf in that this method releases a reference to the interface pointer.Use ComPtr::GetAddressOf when you require the address of the interface pointer but do not want to release that interface.
https://msdn.microsoft.com/zh-cn/library/vs/alm/br230430.aspx
2016.03.12 补充:ComPtr 还有一个 ReleaseAndGetAddressOf 方法, out 语义的参数可以用这个方法取指针地址。而 ComPtr::operator& 方法返回的实际上是 ComPtr 对象指针的一个包装类,该包装类在转换为 void** 或者 T** 时实际上也是调用了 ComPtr::ReleaseAndGetAddressOf 方法。
operator&() 先释放原指针再取地址
GetAddressOf() 直接得到原始指针的地址
显然,operator& 是为COM风格的API设计的,比如下面这种:
HRESULT CreateObject(/* [out] */IUnknown** ppObj);
在这一点上和 ATL::CComPtr 一致,实现上则有不同。上面这种 API 最容易出现的错误是传了一个非空指针的地址给 ppObj ,然后指针被覆盖导致原来指向的对象无法释放。ATL::CComPtr 的解决办法是在 operator& 的实现中加了一个断言,如果指针非空就弹出警告;WRL::ComPtr 的解决办法如上所述,先释放再取地址。
从使用的角度 WRL::ComPtr 更方便,但从设计角度看实际上违背了运算符重载的基本原则。在不看文档的情况下没人能预期到 operator& 会修改原指针的值,这就是坑。
如下面这样一个 API:
HRESUTL SetObjects(/* [in] */IUnknown** ppObjs, /* [in] */int count);
双重指针参数实际代表的是一个指针数组,而非类似 CreateObject 中的指针地址。在数组长度为 1 时,习惯上直接取指针地址设为 ppObjs 参数,然后将 count 设为1 ,如下:
IUnknown* ptr = NULL; // 正确 // ATL::CComPtr<IUnknown> ptr; // 警告 // WRL::ComPtr<IUnknown> ptr; // 悲剧 CreateObject(&ptr); SetOjbects(&ptr, 1); // 注意这里
注意第六行的代码,无论 ptr 是裸指针还是ATL::CComPtr 包装的智能指针都正确的,不过 CComPtr 会引发断言失败使得我们不得不修改成其他实现方式;WRL::ComPtr 智能指针则编译运行一切正常结果却是悲剧。
以后使用 WRL::ComPtr 只能小心些了,只在 out 语义的参数中使用 operator& 其他一律用 GetAddressOf ,或者全部用 GetAddressOf 然后在用作 out 语义的参数前写断言。
设计上的小聪明还是要不得,哪怕是微软。
MSDN 中关于 ComPtr::operator& 运算符的备注说明 :
This method differs from ComPtr::GetAddressOf in that this method releases a reference to the interface pointer.Use ComPtr::GetAddressOf when you require the address of the interface pointer but do not want to release that interface.
https://msdn.microsoft.com/zh-cn/library/vs/alm/br230430.aspx
2016.03.12 补充:ComPtr 还有一个 ReleaseAndGetAddressOf 方法, out 语义的参数可以用这个方法取指针地址。而 ComPtr::operator& 方法返回的实际上是 ComPtr 对象指针的一个包装类,该包装类在转换为 void** 或者 T** 时实际上也是调用了 ComPtr::ReleaseAndGetAddressOf 方法。
相关文章推荐
- C++Const
- 12.3.19 fields ——elasticsearch中文文档
- Python使用mechanize模拟登录、抓取数据的代码
- TOYS(水解析)
- ThreadLocal的原理
- PAT (Advanced Level) Practise 1029 Median (25)
- 20145326《Java程序设计》第一周学习总结
- mongodb3.2安装
- day17
- 9.原型模式
- juery学习总结(一)——juery选择器
- struts2的s:iterator 标签 详解<转>
- nyoj 306 4th河南省赛 走迷宫【dfs】【二分】
- Android Studio中常用设置
- 面试题整理
- android 蓝牙锁应用实例开发(一) 简介
- HTML 表格
- [置顶] C语言代码漏洞审计技巧笔记分享
- 调试小技巧:生成(捕获)存储过程的“形参-实参”对值
- 读《经济解释》卷一