您的位置:首页 > 其它

COM技术内幕-读书笔记-系列之二

2008-12-17 10:27 369 查看
第2章 接口

=================================================================================
未经许可,也可转载,但请注明出处!希望能够与各位初学COM的网友共勉:-)
[b]======================================================================= [/b]

本节提要:本章从接口的一般性概念讲起,由浅入深,一直讲到接口的内存结构,并且介绍了什么是接口、如何实现接口以及如何使用接口。另外还介绍了如何在C++中使用纯抽象基类来实现接口,也知道C++编译器为纯抽象类所生成的内存结构同COM接口所要求的内存结构是相同的。不过本章所讲解的接口还不是真正意义上的COM接口。再次,通过本章的学习,也让我们知道接口可以通过封装其内部实现细节而使一个由组件构成的系统免受外界变化的影响,只要接口不发生变化,那么客户或组件可以在不影响整个系统正常运行的情况下自由的变化。这使得我们可以用新的组件来代替老的组件,客户也可以一致的对待实现同一接口的所有组件。

1、什么是接口?
接口是组件与客户进行通讯的协议,客户通过接口来访问组件。接口有一个二进制标准,也就是说,表示接口的内存块必需具有一定的结构。幸运的是,使用纯抽象基类时,许多C++编译器均可生成具有如此结构的内存块。

2、接口与组件的关系?
接口是组件与客户沟通的窗口,组件本身只不过是是接口实现的细节。一个系统可由多个组件构成,一个组件是一个或多个接口的集合,一个接口是一系列函数的集合。

3、使用COM接口有哪些优点?
1、接口可以保护系统免受外界的影响。
2、接口可以使得客户用同样的方式来处理相同接口的不同组件(这种能力,在C++中被称为多态能力)。
3、接口可以使内部具体实现得以隐藏。

4、如何用C++来实现接口?
下面的代码实现了一个简单的接口,其中组件CA实现了IX和IY接口:



注:
1、 IX和IY是实现接口的纯抽象基类。
2、 纯抽象基类指的是仅包含纯抽象函数的基类。
3、 纯虚函数指的是用“=0”标记的虚函数。
4、 纯虚函数在派生类中必需实现。
5、 对纯虚函数的继承被称为接口继承。
6、 在本书中使用纯虚函数来实现所有的接口。
7、 CA是组件,IX、IY是接口,CA同时实现IX和IY两个接口。
8、 一个COM组件可以有多个接口。
9、 COM接口在C++中是使用纯抽象基类来实现的。
10、一个C++类可以使用多继承来实现一个可以提供多个接口的组件。
11、一个COM组件可以由多个C++类来完成,如可以再包含CFactory类。
12、一个COM组件也可以不包含任何C++类,如用C语言实现。

5、interface是关键字吗?
为了避免将接口定义成一个类的形式,微软在OBJBASE.H中定义如下:
#define interface struct
定义为struct的原因在于struct的成员将自动具有共有属性,因此无需加public造成不必要的混乱。

6、如何在客户端访问组件?
在客户端访问组件,主要是通过组件接口来访问,代码如下:



7、如何用图形来表示接口?
图形化表示接口如下:



8、如何在C++控制台程序中具有MFC的TRACE输出能力?
可以自己定义自己的TRACE函数,如下:
void TRACE(const char * pMsg){cout<<pMsg<<endl;}

9、什么是__stdcall和__cdecl调用约定?

__stdcall:标准调用约定
1、是对Microsoft编译器的一个扩充。任何一个支持支持开发win32程序的编译器都会有此选项或者与之等价的选项。
2、使用__stdcall标记的函数都使用标准调用约定。这些函数在返回到调用者之间将参数从栈中删除。
3、标准调用约定不支持带有变参的函数,如printf函数。
4、几乎所有的win32API函数(除了变参外)都使用标准调用约定,因为标准调用约定可减少代码的大小。
5、大多数编程语言也都使用标准约定,如VB缺省情况下使用标准调用约定。
6、Pascal调用约定等同于__stdcall调用约定。
7、COM接口所提供的所有函数使用的都是标准调用约定。

__cdecl:C调用约定
1、在常规的C/C++调用约定中,栈的清理工作是由调用者完成的。
2、带有变参的函数所使用的约定仍然是C调用约定。

参考:
1、微软在WINDEF.H中将Pascal定义为:
#define pascal __stdcall
2、微软在OBJBASE.H定义STDMETHORDCALLTYPE如下:
#define STDMETHORDCALLTYPE __stdcall

10、本章示例程序存在哪些问题?
1、客户直接使用指向CA的指针:CA *pA=new CA;此举违背了接口原则。
2、使用new和delete等直接控制组件的生命期,
3、new和delete同时违背了组件的语言无关性,因为这都是c++特有的操作。
所以还必须在以后的章节中慢慢消除这些问题。

11、类和组件的关系?
1、类并非组件。
2、一个组件可以由一个或多个C++类实现。
3、一个组件也可以不用任何C++类实现,比如使用C语言实现的组件。

12、类与接口的关系?
1、一个类可以继承多个不同的接口来并实现之。
2、单个类实现单个接口。

13、如何解决接口名称冲突?
当一个组件包含有多个接口时,可能会发生名称冲突,一是接口名称之间的冲突,二是一个接口内部的函数与另外一个接口内部的函数名称的冲突。可以用下列方法来解决:
1、接口名称的冲突可以使用某种简单的约定进行有效的避免。如在接口名称前加公司名称或产品名称。如产品Xyz的IFly接口,可以使用IXyzFly接口名称。
2、函数名称的冲突亦可类似接口名称方法进行解决。如IXyzFly::Fly与IAbcFly::Fly,可以改为:IXyzFly::XyzFly与IAbcFly:: AbcFly
3、不使用多重接口继承。
4、使用包容与聚合技术。
注:COM接口是一个二进制内存标准,客户同接口的连接是通过接口在其表示的内存块中的位置完成的,而不是通过接口名称或其成员函数名称完成。因此COM并不关心接口的名称或其成员函数的名称。

14、如何理解接口的不变性?
接口的不变性是指一旦接口被公布,那么它将永远保持不变。
1、对组件升级时,一般不会修改原有的接口,而是根据需要适当的增加一些新的接口来扩展其原有的功能。
2、通过使用多重接口的继承技术,使得这些组件不但能够支持原有的接口,还可以支持新的接口。因此多重继承为组件和客户可以智能对同对方的新版本进行交互打下了扎实的基础。

15、如何理解接口的多态性?
多态性是指可以按照同一种方式来处理不同的对象。
若两个不同的组件支持同一个接口,那么客户可以使用相同的代码来处理其中任何的一个组件,也就是说,客户可以使用相同的方式来处理不同的组件,从而实现了接口的多态性。
多重接口的支持能力为接口的多态提供了更多的机会,也使得多态的重要性更为突出。因为多态性可以使代码更加有效的被复用。

16、接口内存结构如何定义?



上面所定义的纯抽象基类,实际上定义的是一个内存块结构。其定义如下图所示:



对内存块结构图的说明:
1、pIX指针:IX接口指针,指向vtbl指针。
2、vtbl指针:指向虚拟函数表的指针。
3、虚拟函数表存储的是指向各个成员函数的指针。
4、COM接口的内存结构同C++编译器为抽象类所生成的内存结构是相同的,因此从这个角度来说,IX既是一个COM接口也是一个抽象基类。
5、此内存结构仅仅只是定义,在派生类中实现此基类时才会真正被分配。
6、接口的内存结构在不同的操作系统上可能不同。

17、vtbl指针有何作用?
Vtbl指针在由pIX接口指针到虚拟函数表的过程中,看上去增加了一个额外的级别,有什么作用呢?由于C++编译器生成代码时,实现抽象基类的派生类可能会将特定的实例的信息(如成员数据)同vtbl一起保存。

假设类CA派生于IX,并拥有自己的成员数据m_dA。假设类CB也派生于IX;

单个实例时:pA=new CA ;
pA内存结构图如下(实例数据以黑色背景表示):



拥有两个实例时:pA1=new CA ;pA2=new CA;
两个实例有各自不同的vtbl指针及其数据成员空间(其实vtbl也可以看着是数据成员),但可以共享虚拟表。如下图:



不同的类,继承同一个接口,可以使用相同的虚拟表(vtbl)。
pA=new CA ;pB=new CB;
pIX=pA;//访问CA中的成员函数FXn
pIX=pB;//访问CB中的成员函数FXn
pIX->Fx1;//继承同一接口的所有的类均可被客户按同一种方式处理



注:
1、 上图的实例数据用空框表示,因为COM一般都不关心实例数据。
2、 两个虚拟表的结构相同,即两个表中的函数Fx i所占据的表格项都在第i项。

18、本章代码如何在VC6的工程中运行?
本章实例代码比较简单,按下列步骤即可:
1、新建一个空的console空的工程。
2、将随书源代码中的iface.c文件COPY到该工程目录下。
3、将该文件加入到该工程中。
4、编译运行即OK。

BENKONG2008-12-16
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: