COM的由来(一)

为了达到组件复用,让我们看看为什么COM才是最好的选择.
1.
写成一个类,通过复制.h,.cpp文件到达组件复用
    
缺点很明显,如果该类有什么变化,所有相关的组件都要复制一次,当相关的组件很多的时候,累人还容易出错误.

2..lib发布类库
  
每次改变都需要相关组件重新编译。当项目越来越大(编译一次都要半个小时),会极大的影响效率.一般只需要把变更比较少,用的比较多的类做成静态库即可.

3.不需要编译,动态链接,把实现类导出(_declspec(dllexport))
   1)
各个c++编译器所产生的dll并不通用(因为c++中允许操作符和函数重载,c/c++编译器都有自己的规则去修改每一个入口点的符号(名字改编技术name mangling),以便允许同一个名字对应不同的函数调用)改善该问题,可以使用模块定义文件.但是却不能捕捉异常.
   2)
类扩充的时候(即sizeof(class)增大的时候),用新的类声明调用老版本的dll时,就会出现问题(极有可能出现操作不合法地址的情况).
  
也可以采用实现和接口分离的技术解决这个问题(即在接口类中定义个实现类的指针)
.
但是也有缺点:使代码变的低效(多了一层函数调用),当方法很多时,增加了出错的可能.
  

4.为了解决以上在实现组件复用面临的困难,可以根据c/c++vptr/vtbl结构(所有的编译器都是一样的),仅仅只是暴露接口(声明为纯虚函数),而让派生类去实现.
比如:
interface ITest
{
virtual  void doSomething()=0;
};

class TestClass: public ITest
{
void doSomething();
};

extern "C" _declspec(dllexport) ITest * GetInterface()
{
return dynamic_cast<ITest *>(new TestClass);
}

:extern "C" 是表示该导出函数采用c的规则(所有编译器都一样)去解析函数名

  但当派生类中需要动态分配和释放内存的时候,会造成内存泄露.如果把接口类的析构函数声明为虚函数,又破坏了dll的通用性(虚析构函数在vtbl中的位置会随着编译器不同而不同).可以在接口类中声明一个接口,让派生类去实现释放内存(IUnknownRelease()出现的原因).此时接口变成如下样子:
interface ITest
{
virtual  void Release()    =0;
virtual  void doSomething()=0;
};


 
当接口的方法增加后(写的顺序要一致,这关乎虚函数在vtbl里的位置),新客户在使用老对象去调用新方法的时候,又会出现问题(同前3(2)).这个时候可以根据用户请求的接口去进行相应的转化(使用dynamic_cast)
:
interface ITestNew
{
virtual  void Release()    =0;
virtual void  New()=0;
};

class TestClass :public ITest,public ITestNew
{
void  doSomething();
void  New();
void  Release();
};
如果你想调用新的方法,可以这样:
bool NewMethod(ITest *pOld)
{
ITestNew *pNew = dynamec_cast<ITestNew *>(pOld);
if(pNew) {pNew->New(); return true};
return false;
};
此时又出现了问题,对于RTTI每个编译器的实现是不一样的。解决办法可以显式的暴露一个接口的名字,由这个去完成RTTI,从而实现与编译器无关.
interface ITestNew
{
virtual void Release()   =0;
virtual void *Dynamic_Cast(const char *pszTypeName)=0;
virtual void  New()=0;
};

既然ITestITestNew都有共同的方法,很自然的我们把这些方法提升到一个基接口中,其他所有接口都从这个接口继承,此时COM已经初局规模:).
interface IPulibcTest
{
virtual void Release()   =0;
virtual void *Dynamic_Cast(const char *pszTypeName)=0; 
};


interface ITest :public IPulibcTest
{
virtual void  doSomething() = 0;

virtual void  New() = 0;
};

class     TestClassNew:public ITestNew
{
//具体去实现
...........
};

有了这样的类层次之后,客户就可以利用编译器的独立结构,动态的查询dll是否实现了某个指定的接口:
bool NewMethod(ITest *pTest)//COM中一般采用返回HRESULT值去确定方法是否可以调用
{
ITest *pNew = (ITest *)pTest->Dynamic_Cast("ITestNew ");
if(pNew)
{ pNew ->New();return true;}   return false;  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值