摘要
这篇文章考察了使用C++巧妙的接口指针去访问元件对象模型(COM)接口。SICBENT样本应用程序与这篇文章相关。
介绍
很久以前,在非常黑暗的过去,Nigel Thompson写了一系列关于被称为"OLE傻瓜书"(MSDN库,技术文章)的OLE编程的技术笔记。当时走廊的上上下下都能听到他痛苦地叫喊着忘记了要正确地添加或释放一个接口。我想本应该有一些方法在使用C++中巧妙的指针时能自动释放元件对象模型(COM)元件,使得元件对象模型元件的使用更为容易一些。不过,我开始研究的却是Microsoft 基础类库(MFC),在这个课题中引用计算并不是一个重点,因为它隐藏在MFC类之中。
在写完文章"MFC/COM对象8:重访无MFC的多重继承"之后,我决定再考虑使用巧妙的指针类简化使用元件对象模型(COM)接口。研究的结果并没有实现我的愿望,我开始怀疑是否能在我自己的代码中使用巧妙的指针接口。不过,你的元件对象模型(COM)项目可能与我的不一样,所以我决定不用元件对象模型(COM)元件也许并不会影响你。
在这篇文章中,我将讨论下列主题:
创建一个灵巧的接口指针类的原因
如何创建一个巧妙的接口指针类
使用巧妙的接口指针类
我不喜欢巧妙的接口指针类的原因
在这篇文章的源代码中,我使用前缀PI指示一个指向接口的指针,例如:
IPersist* pIPersist ;
I use the prefix SI to refer to a smart interface pointer:
CSmartInterface
创建一个巧妙的接口指针类的原因
我想要一个巧妙的接口指针类的原因是要自动地添加和释放接口指针。
使用元件对象模型(COM)接口指针时,你必须要遵循几条规则。首先,绝不要在一个接口指针上调用删除(delete)来代替Release。下面的代码是不正确的:
IDraw* pIDraw ;
CoCreateInstance(...,IID,_IDraw, (void**)&pIDraw) ;
pIDraw->Draw(0,0,100,100) ;
delete pIDraw ; // Don't delete an interface pointer.
下面的代码是正确的:
IDraw* pIDraw ;
CoCreateInstance(...,IID,_IDraw, (void**)&pIDraw) ;
pIDraw->Draw(0,0,100,100) ;
pIDraw->Release() ;
C++程序设计员通常delete一个对象指针。因为这个原因,C++程序员容易忘记并且在一个接口指针上用delete代替Release。它也是C++程序员很难发现的一个错误,因为删除指针太自然了。
第二条规则是在创建新的指针时调用AddRef 。下面的代码是不正确的:
IDraw* pIDraw ;
CoCreateInstance(...,IID,_IDraw, (void**)&pIDraw) ;
pIDraw->Draw(0,0,100,100) ;
delete pIDraw ; // Don't delete an interface pointer.
下面的代码是正确的:
IDraw* pIDraw ;
CoCreateInstance(...,IID,_IDraw, (void**)&pIDraw) ;
pIDraw->Draw(0,0,100,100) ;
pIDraw->Release() ;
上面的例子太小了,但是,在复杂的代码中,这个错误是很难跟踪到的。
使用巧妙的接口指针类并不只有唯一一个原因。DonBox在C++报道中提到了其他的一些原因(请参阅本篇文章末尾的书目)。
如何创建一个巧妙的接口指针类
一个巧妙的接口指针类开始创建的方式与一个巧妙的指针类是一样的:通过为一个类执行操作符-》。这个处理过程也可被称为委派。通过覆盖操作符->,我们可以做一个类模仿一个指针调用。例如:
void Draw()
{
CSmartInterface
CoCreateInstance(...,IID,_IDraw, (void**)&SIDraw) ;
SIDraw->Draw() ;
}
在上面的代码中有几点需要注意。首先,一个模板类被用于执行巧妙的接口指针。这使得巧妙的指针接口类是个安全类型。第二,就象我们就要看到的,操作符-已经被超载去返回一个包括在CsmantInterface中的指针的地址。第三,即使SIDraw不是一个指针,我们也使用操作符->去调用Idraw接口中的成员。第四,我们不调用Release是因为在栈上已经创建了CxmartInterface,析构函数会自动调用Release。
下面是CsmartInterface头文件中重要的部分,所以的成员函数和操作符都在头文件中稍后的部分中定义了。
CsmartInterface包含着一个指向一个接口的指针。为了类型安全,它被定义为一个模板函数。CsmartInterface的实质是超载操作符->。
template
class CSmartInterface
{
public:
// Construction
CSmartInterface(I* pI = NULL) ;
// Copy Constructor
CSmartInterface(const CSmartInterface& rSI) ;
// Destruction
~CSmartInterface() ;
// Assignment from I*
CSmartInterface& operator=(I* pI) ;
//
// Operators
//
// Conversion
operator I*() ;
// Deref
I* operator->() ;
// Address of
I** operator&() ;
// Equality
BOOL operator==(I* pI) const;
// Inequality
BOOL operator!=(I* pI) const;
// Negation
BOOL operator!() const ;
protected:
I* m_pI ;
};
所以,来自前一个例子的SIDraw->Draw()导致了对SIDraw.m_PI->Draw()的调用。SIDraw把Draw调用委派给m-PI指向的接口。这种方式的强大之处在于CsmartInterface
