COM接口与对象学习心得150901由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“学习心得面向对象”。
COM对象与接口学习心得引言
COM是Microsoft提出的一种二进制兼容构件的规范,只要遵守这种规范,不管用什么编程语言和工具开发的COM构件,也不管是否运行在同一台机器上,还是运行在不同的机器上,都可以被使用。COM是建立在二进制层次上的调用标准,与编程语言和开发工具无关;COM定义了大量的标准规范接口(如IUnknown、IClaFactory、IDispatch等等)用于各种不同的用途。如今,COM成了微软跟上因特网快速发展的重要基础技术,在windows平台上,COM随处可见。
COM对象是指符合面向对象设计中对象的基本概念,通过COM接口提供服务的COM组件的实例。COM接口是客户与对象之间的通信协议,对象实现COM接口,客户调用COM接口。COM对象由GUID唯一标识,通常可以实现多个接口。COM接口由CLSID标识,它不能被独立使用,要求必须存在于某个COM对象上,COM接口提供的功能是与语言、平台和编译器无关的。静态链接与动态链接
C++程序的链接按照时间可以分为静态链接和动态链接。动态链接是在运行时与库函数进行链接,它使得不同的程序开发者能够相对独立地开发和测试自己的程序模块,从某种意义上来讲大大促进了程序的开发效率,原先限制程序的规模也随之扩大;静态链接是在编译的时候与库函数进行连接,会将静态库中的所有方法都编译到应用程序中。C++的动态链接是符合COM接口设计需要的。
静态链接的优点:代码装载速度快,执行速度略比动态链接库快; 只需保证在开发者的计算机中有正确的.LIB文件,在以二进制形式发布程序时不需考虑在用户的计算机上.LIB文件是否存在及版本问题,可避免DLL地狱等问题。缺点:使用静态链接生成的可执行文件体积较大,包含相同的公共代码,造成浪费。
动态链接优点:更加节省内存并减少页面交换;DLL文件与EXE文件独立,只要输出接口不变(即名称、参数、返回值类型和调用约定不变),更换DLL文件不会对EXE文件造成任何影响,因而极大地提高了可维护性和可扩展性;不同编程语言编写的程序只要按照函数调用约定就可以调用同一个DLL函数;适用于大规模的软件开发,使开发过程独立、耦合度小,便于不同开发者和开发组织之间进行开发和测试。缺点:使用动态链接库的应用程序不是自完备的,它依赖的DLL模块也要存在,如果使用载入时动态链接,程序启动时发现DLL不存在,系统将终止程序并给出错误信息。而使用运行时动态链接,系统不会终止,但由于DLL中的导出函数不可用,程序会加载失败;速度比静态链接慢。3 客户与对象之间的通信
C++的封装是语法上的封装,而不是二进制封装,其对象创建与释放的运算符new/delete是编译器相关的,编译器不仅要知道类的public信息,也要知道private信息。C++对象的二进制结构是编译器相关的,即使客户看到的C++类公开接口没有变化,但是C++类的实现改变了,仍然会打破客户与对象之间的连接。
在对COM接口进行设计时,应使客户与C++对象之间的连接点越小越好,尽可能地只有接口部分必要的信息才放入接口,把C++类的实现细节与接口分开,提取出针对所有编译器都不变的因素作为客户与对象共享的接口信息。
C++纯虚基类的设计可以很好地解决COM接口的设计问题。纯虚基类只包含了虚函数,限定每个虚函数的调用规则,在给定的平台上所有的编译器都会产生同样的二进制结构。对于跨平台的情形,需要通过中间层,这里暂不考虑。纯虚基类的使用解决了方法实现的命名冲突,也解决了C++类中二进制布局不兼容的问题,客户只能看到vtable,没有看到其他的实现细节,保证了不同语言编写的程序可以互操作,也可以在不改变接口的情况下,可以单独升级客户或者对象。
例如定义一个接口IString,它包含了两个纯虚方法。cla IString {
virtual const char*Find(const char *psz)=0;
virtual int Length()=0;};
类CMyString实现了IString定义的接口(具体实现略)。cla CMyString : public IString { private:
char *m_psz;public:
CMyString(const char * psz);
~CMyString();
const char*Find(const char *psz);
int Length();};
如果不使用C++的运算符,客户如何得到IString的虚表接口呢?可以提供一个引出函数供客户使用,隐藏创建对象的内部细节。
extern “C” _declspec(dllexport)IString *CreateString(const char *psz);extern “C” IString *CreateString(const char *psz){
return new CMyString(psz);}
客户访问vtable接口举例: #include “istring.h” typedef IString *(*PfnCreateString)(const char *psz);void main(){
IString *p;
HANDLE h = LoadLibrary(“c: empmystring.dll”);
if(NULL!=h){
PfnCreateString pfn =
(PfnCreateString)GetProcAddre(h,“CreateString”);
if(pfn){
p = pfn(“Hello”);
if(p){
const char*psz = p->Find(“llo”);
int n = p->Length();
}
}
} };
至此,客户避开了C++运算符的编译器相关性,访问了IString接口,这样就建立了对象与客户之间的通信方式。IUnknown接口
COM定义的每一个接口都由IUnknown接口继承而来,由于IUnknown接口提供了两个重要的特性:生存期控制和接口查询。客户程序只能通过COM接口与COM对象通信,虽然客户不用关心对象内部对方法的实现细节,但是它必须控制对象的存在与否。cla IUnknown { public: virtual HRESULT__stdcall QueryInterface(const IID& iid, void **ppv)= 0;virtual ULONG __stdcall AddRef()= 0;
virtual ULONG __stdcall Release()= 0;};
每个COM对象要管理一个被称为引用计数(reference count)的整数值。每个引用从被有效赋值开始,一直到生命周期结束,这期间被称为:outstanding reference。为了有效地管理对象的生命周期,COM提供一些规则和操作,供客户遵守和使用:保持引用计数的确切含义,也就是记录当前outstanding reference的数目;引用计数从0开始,首次把接口递交给客户时为1,以后由客户管理,当引用计数回到0时,删除自己。
当客户通过复制获得新的接口指针时,引用计数加一,当某个接口不用时,减一。
3.1 COM接口的内存模型
COM接口的内存模型如下图所示,客户首先通过创建对象来获得接口指针,然后利用接口指针来访问vtable中的方法,vtable方法由COM对象内部实现。
3.2 查询接口
一个COM对象可以实现多个接口。从一个接口到另一个接口的访问途径可以通过函数QueryInterface(iid, ppv)来实现。初始得到了一个接口指针之后,调用它的QueryInterface函数,获得另一个接口指针。这里要注意:IUnknown必须是个静态接口指针,其他接口指针可以是动态的;QueryInterface在返回接口之前,必须调用新接口的AddRef函数,以示该接口的引用计数加1。
3.3 对象生存期与引用计数
引用计数是为了控制对象的生命周期,多个客户可以独立地控制对象的生存周期。引用计数反映了被客户引用的个数Outstanding references。引用计数在对象被创建时赋值为0,在接口被复制时加1,接口释放时减一,当引用计数为0时,表示没有客户在使用对象或者接口,则释删除对象或接口。
引用计数的实现由三个层次,组件级、对象级、接口级。组件级引用计数分辨率太粗糙,不容易控制,而接口级引用计数分辨率太细,效率较低。
引用计数规则:在顺序执行过程中,如果要对一个接口指针变量赋值,则对赋值后的接口指针变量调用AddRef,并且,如果赋值前的接口指针变量还没有结束,则赋值前必须对它调用Release以便先结束它的使用;如果要结束使用一个接口指针变量,以后不再用到它了,则调用Release函数。
引用计数注意事项:在整个生存期内,AddRef与Release一定要配对,否则漏掉AddRef,程序出错,漏掉Release,对象永不释放,造成内存泄露;对象的释放由Release内部实现,AddRef和Release的返回值并不可靠,不能准确的反映Outstanding references。