有效的使用和设计COM智能指针——条款11:以类型安全的方式创建资源和查询接口
来源:互联网 发布:淘宝高仿衣服店推荐 编辑:程序博客网 时间:2024/06/05 22:07
条款11:以类型安全的方式创建资源和查询接口
下面这种写法在COM组件编写过程中这种错误的写法并不少见:
void func(){ IX *pIX = NULL; hrRetCode = CoCreateInstance( CLSID_MYCOMPONENT, NULL, CLSCTX_INPROC_SERVER, IID_IX (void **)pIX //额~~阴沟里翻船了~ ); assert(hrRetCode); pIX->IxFunction();}
上述代码会发生什么?其行为不确定,而且在大多数情况下是错误的。原因是由于创建COM组件或者接口查询的时候使用的函数并非类型安全的。
针对这种情况,你可能会想到我们应该多利用智能指针来避免这一问题。因为智能指针无法强制转换成(void**)类型。貌似这样做能使得你的类型变得安全一些,然而错误还是发生了:
void func(){ CComPtr<IX> spIX = NULL; hrRetCode = CoCreateInstance( CLSID_MYCOMPONENT, NULL, CLSCTX_INPROC_SERVER, IID_IY //额~~这里又出错了 :( (void **)&pIX ); assert(hrRetCode); pIX->IxFunction();}好在后面的断言能将错误及时的反馈上来,但是如果我们有更好的方法来避免这一问题的出现,为何不用呢?
解决这一问题的最好方式是用智能指针所提供的接口查询方法:
void func(){ CComPtr<IX> spIX = NULL; //IID在智能指针创建的时候与其绑定在一起 spIX .CoCreateInstance(CLSID_MYCOMPONENT); assert(spIX ); pIX->IxFunction();}
是的,这一点没错。我们推荐使用智能指针,但是我们更多的希望的是你通过智能指针提供的功能性函数来完成资源的创建与查询。他不仅带来了了代码上的简洁,而且使得你的代码在类型上更加的安全。
有时候,我们在考虑设计某个类时,通常会考虑他的移植性和兼容性。这导致我们会慎重的采用一些与编译器相关的特性时,往往采用了谨慎的态度。或者更多时候,我们避讳编译器给我们带来的某些特性,来追求可移植和不同平台下的兼容性。
但值得注意的是,在考虑这些问题之前,我们先应该考虑的是程序的正确执行。一个允许错误肆意存在的程序,即便是可以随意移植,意义也不会很大。
首先看一眼下面这套接口和GUID的定义:
// {994D80AC-A5B1-430a-A3E9-2533100B87CE}DEFINE_GUID(IID_ICALCULATOR, 0x994d80ac, 0xa5b1, 0x430a, 0xa3, 0xe9, 0x25, 0x33, 0x10, 0xb, 0x87, 0xce); class ICalculator public : IUnknown{public: virtual HRESULT STDMETHODCALLTYPE Add( const int nNum1, const int nNum2, int *pnSum ) const = 0; virtual HRESULT STDMETHODCALLTYPE Sub( const int nMinuend, const int nSubtrahend, int *pnQuotient ) const = 0;};
这样做没有问题,而且在使用某些智能指针的时候也不会存在问题。如_com_ptr_t允许你在特异化一个智能指针的时候采用如下这种方式将IID和接口绑定起来。
//特异化一个智能指针的时候采用如下这种方式将IID和接口绑定起来_COM_SMARTPTR_TYPEDEF(ICalculator, IID_ICALCULATOR);HRESULT Calculaltor(){ ICalculatorPtr spCalculator (CLSID_CALCULATOR); //构造函数可创建COM组件 int nSum = 0; spCalculator->Add(1, 2, &nSum); return S_OK;}
采用这种方式即便是在特异化时候,不小心将IID和对应ICalculator那么查找和修改起来也会相对于简单一点。而且智能指针一经声明,则将接口指针和IID就绑定在了一起。后续开发便不需要考虑他们之间匹配的问题。
但是如果上述接口声明和CComPtr配合使用,情况就大为不一样了。你的程序可能根本无法通过编译:
void func(void){ CComPtr<ICalculator> pCalculator = NULL; //编译失败,提示:no GUID has been associated with this object hrRetCode = pCalculator .CoCreateInstance(CLSID_CALCULATOR,); assert(hrRetCode); spCalculator->DoSomething();}
可以看出CComPtr 和 _com_ptr_t 采用了两种不同的方式解决IID和接口的绑定问题。CComPtr需要开发人员在接口声明的时候将IID与接口绑定。而_com_ptr_t则可以根据需要决定是否使用__uuid。
因此为了兼容这两种智能指针我们需要在定义的时候使用如下这种方式:
//使用编译器为我们提供的安全机制指定IIDinterface __declspec(uuid("994D80AC-A5B1-430a-A3E9-2533100B87CE")) ICalculator : IUnknown{ virtual HRESULT STDMETHODCALLTYPE Add( const int nNum1, const int nNum2, int *pnSum ) const = 0; virtual HRESULT STDMETHODCALLTYPE Sub( const int nMinuend, const int nSubtrahend, int *pnQuotient ) const = 0;};
如果你懂得IDL,那么用IDL定义接口并将IID与其绑定会更加合理一些。但暂且让我们这样做,它会使示例想表达的东西更加明确和清晰。
是的,这样虽然移植性稍差一点,但是它使得接口使用起来更为安全和方便。你不需要在每次查询接口和创建COM组件的过程中为接口指定相应的IID。那常常是导致错误的地方。我们需要的是先考虑如何编写难以发生错误的安全代码,之后才是其兼容性。
如果你想了解如何解决uuid和__uuidof所带来的移植和兼容性的讨论。看以参看条款25。
- 有效的使用和设计COM智能指针——条款11:以类型安全的方式创建资源和查询接口
- 有效的使用和设计COM智能指针——条款6:尽量以智能指针替换接口指针
- 有效的使用和设计COM智能指针——条款15:以原则中的优先级作为取舍的依据
- 有效的使用和设计COM智能指针——条款10:尽量减少智能指针和接口指针的混用
- 有效的使用和设计COM智能指针——条款26:自动查询接口带来方便同时也潜藏危机
- 有效的使用和设计COM智能指针——条款14:有意识的限制智能指针的生命周期
- 有效的使用和设计COM智能指针——条款1:智能指针之前世今生
- 有效的使用和设计COM智能指针——条款21:巧妙的将对象伪装成指针
- 有效的使用和设计COM智能指针——条款20:安全的覆盖掉C++默默为我们编写的函数
- 有效的使用和设计COM智能指针 ——条款5:了解_com_ptr_t 设计背后的历史原因
- 有效的使用和设计COM智能指针——条款24:努力使得接口容易被使用而不易被误用。
- 有效的使用和设计COM智能指针——条款19:在接口完满的前提下使之最小化。
- 有效的使用和设计COM智能指针——条款2:引用计数的是与非
- 有效的使用和设计COM智能指针——条款4:理解ATL的CComPtr提倡简单,高效
- 有效的使用和设计COM智能指针—条款4:理解ATL的CComPtr提倡简单
- 有效的使用和设计COM智能指针 ——条款16:智能指针的引入不能违反COM引用计数规则
- 有效的使用和设计COM智能指针——条款18:重载运算符不应当扭曲其语义
- 有效的使用和设计COM智能指针——条款23:为例外条件准备应对策略。
- 10大负面测试用例(转帖)
- cout使用到一些小知识点回顾。
- sql日期格式化
- 破解Visual SVN
- 找到一些宝贝
- 有效的使用和设计COM智能指针——条款11:以类型安全的方式创建资源和查询接口
- Hibernate的dialect大全
- C++里面保护宏的用法
- placement new, operator new and new operator
- C/C++处理错误
- 重读 Spring 3 开发手册 总结 一
- web测试入门
- 第二季
- hadoop ganglia configuration