COM的由来(二)

来源:互联网 发布:兄弟网络 编辑:程序博客网 时间:2024/05/17 07:03

上一次说到了virtual void *Dynamic_Cast(const char *pszTypeName)=0; 函数

而实际中的COM却是这样的:

HRESULT  QueryInterface(const  GUID & iid,void  ** ppObj) =0;

这里引入了GUID,即Globally   Unique  Identifier,即全球唯一标识符,来确定组件的唯一性.

引入GUID是基于这样考虑: 如果单纯根据接口名字去返回接口指针,肯定会存在重复的情况。即当不同的程序员开发同一个接口时,可能在实现方法的顺序和原型上存在差别,不兼容,就会产生错误.

当我们需要单个对象支持多个接口的时候.比如如下代码:

interface ITestOther :public IPulibcTest

{

virtual  void   DoSthOther()   = 0;

}

class  TestClass:public  ITestOther ,public ITest

{

//具体实现

}

//使用该组件DoSthOther()方法

void  fun(){

ITest *pTest = NULL;

ITestOther *pOther = NULL;

pTest = CreateTestInterface();

//该函数的实现可以采用一般dll引用导出函数方法调用前面的GetInterface函数

if(pTest ){

pOther = (ITestOther *)pTest->Dynamic_Cast("ITestOther");

if(!pOther)  pTest->Release();//(1)

else {

 pOther->DoSthOther(); pOther->Release(); //(2)

//1,2处,虽然调用的都是同一个函数,写法却不同

}

}

此时,必须记录下哪个指针与哪个对象联系在一起,并且每个对象只能调用一次Release方法.

当复杂的代码中,管理这些关系的成本将大幅度提升.一个简单的解决办法就是让每个实现对象维护一个引用计数,当接口指针复制的时候,该值增加;当接口指针销毁时,该值减少.(AddRef终于也面世了:))

即如下:

class  TestClass:public  ITestOther ,public ITest

{

long m_cRef;

TestClass: m_cRef(0) {}

int  AddRef() {m_cRef++;}   //要想改组件在多线程中也适用,可以改成InterlockedIncrement(&m_cRef);

int  Release() { if(--m_cRef==0) delete this;} //改成多线程版本,同上

//具体实现

}

注:一般我们可以把AddRef()和Release隐藏到c++ smart pointer指针后面

这样fun函数就变成如下情况了:

void  fun(){

ITest *pTest = NULL;

ITestOther *pOther = NULL;

pTest = CreateTestInterface();

if(pTest ){

pOther = (ITestOther *)pTest->Dynamic_Cast("ITestOther");

if(pOther)  {

 pOther->DoSthOther(); pOther->Release();

}

pTest->Release();

}

到这里,COM的3位大佬终于现身完毕了,挥把汗先:)

后记: COM 把接口与实现分开的动机,是把对象内部的工作细节隐藏起来。这样就可以允许实现类中的数据成员的数量和顺序随意变化,但使用改dll的程序无须编译就可以继续使用.也允许程序在运行时刻询问对象以便知道对象是否支持新的功能。并且采用COM生成的dll与编译器无关.

尽管如此,它还不足以"为二进制组件提供一个普遍的低层基础“。不管怎么样,该组件必须是使用C++编译器的程序才能引用它.为了组件达到与语言无关的目的,COM引入了一个跟C语法相似的语言:接口定义语言(Interface

Definition   Language,IDL)

原创粉丝点击