设为首页 加入收藏

TOP

有效的使用和设计COM智能指针――条款6:尽量以智能指针替换接口指针 (二)
2014-11-23 23:18:04 来源: 作者: 【 】 浏览:7
Tags:有效 使用 设计 COM 智能 指针 条款 尽量 替换 接口
erface
if (SUCCEEDED(hr)) {
hr = GetObject(&rgpUnk[2]);
if (SUCCEEDED(hr)) {
UseObjects(rgpUnk[0], rgpUnk[1], rgpUnk[2]);
rgpUnk[2]->Release();
}
rgpUnk[1]->Release();
}
rgpUnk[0]->Release();
}
}
void f(void) {
IUnknown *rgpUnk[3];
HRESULT hr = GetObject(&rgpUnk[0]);
if (SUCCEEDED(hr)) {
hr = GetObject(&rgpUnk [1]); //为了使得代码简单这里用GetObject代替QueryInterface
if (SUCCEEDED(hr)) {
hr = GetObject(&rgpUnk[2]);
if (SUCCEEDED(hr)) {
UseObjects(rgpUnk[0], rgpUnk[1], rgpUnk[2]);
rgpUnk[2]->Release();
}
rgpUnk[1]->Release();
}
rgpUnk[0]->Release();
}
}

我并不觉得你能一眼看清楚上面代码的关键所在。只有当你一行一行读下来,你才会恍然大雾“原来只是为了调用UseObjects(rgpUnk[0], rgpUnk[1], rgpUnk[2])这个函数”。而他需要的是三个COM接口指针。于是出现了这种层层嵌套的代码,以及嵌套之后的Release调用。你或许会用大量的精力来考虑括号是否配对,Release和GetObject是否成对出现。或许在它还使得你不得不拖动IDE下方或者右侧的滚动条来查看后续代码。

他不仅让人眼花,更重要的是他找不到关键逻辑代码。智能指针能简化这个编写过程,而且十分优雅:

view plaincopy to clipboardprint void f(void) {
CComPtr rgpUnk[3];
if (FAILED(GetObject(&rgpUnk[0]))) return;
if (FAILED(GetObject(&rgpUnk[1]))) return;
if (FAILED(GetObject(&rgpUnk[2]))) return;

UseObjects(rgpUnk[0], rgpUnk[1], rgpUnk[2]);
}
void f(void) {
CComPtr rgpUnk[3];
if (FAILED(GetObject(&rgpUnk[0]))) return;
if (FAILED(GetObject(&rgpUnk[1]))) return;
if (FAILED(GetObject(&rgpUnk[2]))) return;

UseObjects(rgpUnk[0], rgpUnk[1], rgpUnk[2]);
}

少了多余的AddRef()和Release(),世界清静了,你看到了UserObjects这个关键的逻辑,

或许你现在已经对智能指针跃跃欲试,因为它可以获取如此多的好处,而代价却相当之少(它只需要在函数堆栈上开辟一个极小的空间用于存放智能指针对象,大小往往也和普通指针的大小相同)。但在此之前,我们来看一些更加令人兴奋的特性。

观察下面代码:

view plaincopy to clipboardprint HRESULT hrRetCode = E_FAIL;
IX *pIX = NULL;
hrRetCode = CoCreateInstance(
CLSID_MYCOMPONENT,
NULL,
CLSCTX_INPROC_SERVER,
IID_IY, //哦~ 真悲剧,传错了IID。
(void **)&pIX
);
KG_COM_ASSERT_EXIT(hrRetCode);
pIX->fun();
HRESULT hrRetCode = E_FAIL;
IX *pIX = NULL;
hrRetCode = CoCreateInstance(
CLSID_MYCOMPONENT,
NULL,
CLSCTX_INPROC_SERVER,
IID_IY, //哦~ 真悲剧,传错了IID。
(void **)&pIX
);
KG_COM_ASSERT_EXIT(hrRetCode);
pIX->fun();

如果你仔细查看便会发现,查询接口的时候用IID_IY却用IX类型的指针作为参数接收。类似的错误还有可能是你查询的是IX但是用的是IY的接口进行接收。对于这样的错误代码,执行之后会发生什么,这实在没有什么值得我们深入研究的必要。而我们考虑得更多的应该是研究避免这一问题的方法。

首先来探究一下上述错误原因的关键:

1.IID 和接口类型没有静态的绑定在一起,这可能导致IID和接口的错误搭配。

2.CoCreateInstance的传出参数(最后一个参数)是void**类型,因此他是类型不安全的,完全有可能将任意类型的接口错误传入。

解决问题的办法仍然是智能指针。看一下下面这个优雅的方案,类型安全的问题似乎可以得到解决。

view plaincopy to clipboardprint HRESULT hrRetCode = E_FAIL;
CComPtr spIX
hrRetCode = spIX.CoCreateInstance(CLSID_MYCOMPONENT);//不存在IID和void**了
KG_COM_ASSERT_EXIT(hrRetCode);
pIX->fun();
HRESULT hrRetCode = E_FAIL;
CComPtr spIX
hrRetCode = spIX.CoCreateInstance(CLSID_MYCOMPONENT);//不存在IID和void**了
KG_COM_ASSERT_EXIT(hrRetCode);
pIX->fun();

以上代码中在智能指针后加“.”的用法貌似会让你对“指针”这个概念产生疑惑。你可能会问它不应该是->操作符吗?我们会在后面章节的讨论中涉及这个问题。暂且你不妨将智能指针理解为一个资源管理对象,而这个对象填充了一个安全创建COM组件的方法。类似的操作还存在智能指针对于QueryInterface这类操作中。

有些智能指针提供给我们一种更为方便的方式来创建COM组件和查询接口。如_com_ptr_t可以如下这种方式创建COM组件:

view plaincopy to clipboardprint _COM_SMARTPTR_TYPEDEF(ICalculator, __uuidof(ICalculator));
ICalculatorPtr spIX(CLSID_MYCOMPONENT);
KG_ASSERT_EXIT(spIX);
spIX->fun();
_COM_

首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇按照单词翻转字符串,不改变单词.. 下一篇有效的使用和设计COM智能指针――..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: