COM编程之二 接口
【1】接口
DLL的接口是它所输出的那些函数。
C++类的接口是该类的一个成员函数集。
COM接口是包含一个函数指针数组的内存结构。
每一个数组元素包含的是一个由组件所实现的函数的地址。
在COM中接口就是一切。对于客户而言,一个组件就是一个接口集。
客户只能通过接口才能同COM组件打交道。
对程序员而言,接口对于一个应用程序是最重要的,组件本身只不过是接口的实现细节。
一个接口是一个函数集合,一个组件则是一个接口集,而一个系统则是一系列组件的集合。
如下图更为精确地显示了接口对于COM程序的重要性:
【2】COM接口的优点
1、COM接口可以保护系统免受外界变化的影响。
2、COM接口使得客户可以用同样的方式来处理不同的组件。(这种能力即多态)
3、COM接口可以使内部具体实现得以隐藏。
【3】COM接口的C++实现
实现代码如下:
class IX //First interface
{
public:
virtual void Fx1() = ;
virtual void Fx2() = ;
}; class IY //Second interface
{
virtual void Fy1() = ;
virtual void Fy2() = ;
}; class CA : public IX, public IY //Component
{
public:
//Implemention of abstract base calss IX
virtual void Fx1() { cout << "Fx1" << endl; }
virtual void Fx2() { cout << "Fx2" << endl; }
//Implemention of abstract base class IY
virtual void Fy1() { cout << "Fy1" << endl; }
virtual void Fy2() { cout << "Fy2" << endl; }
};
可见,IX和IY是实现接口的纯抽象基类。
纯抽象基类指仅包含纯虚函数的基类。
纯虚函数在派生类中必需实现。否则 派生类仍不可实例化。
对纯虚函数的继承被称为接口继承。
CA是组件,IX、IY是接口,CA同时实现IX和IY两个接口。
一个COM组件可以有多个接口。由多重继承实现。
COM接口在C++中是使用纯抽象基类来实现的。
一个C++类可以使用多继承来实现一个可以提供多个接口的组件。
一个COM组件可以由多个C++类来完成,如可以再包含CFactory类。
一个COM组件也可以不包含任何C++类,如用C语言实现。
IX和IY并非真正意义的COM接口,真正的COM接口必须继承一个名为IUnknown的接口。
【4】识别interface关键字的本质
为了避免将接口定义成一个类的形式,微软在OBJBASE.H中定义如下:
#define interface struct
定义为struct的原因在于struct的成员将自动具有共有属性,因此无需因加public造成不必要的混乱。
【5】图形化表示接口与组件
图形化表示接口与组件。如下图所示:
【6】如何解决名称冲突?
当一个组件包含有多个接口时,可能会发生名称冲突。
一是接口名称之间的冲突,
二是一个接口内部的函数与另外一个接口内部的函数名称的冲突。
可以用下列方法来解决:
1、接口名称的冲突可以使用某种简单的约定进行有效的避免。
如在接口名称前加公司名称或产品名称。
如产品Xyz的IFly接口,可以使用IXyzFly接口名称。
2、函数名称的冲突亦可类似接口名称方法进行解决。
如IXyzFly::Fly与IAbcFly::Fly,
可以改为:IXyzFly::XyzFly与IAbcFly:: AbcFly
3、不使用多重接口继承。
4、使用包容与聚合技术。
注:COM接口是一个二进制内存标准,客户同接口的连接是通过接口在其表示的内存块中的位置完成的,而不是通过接口名称
或其成员函数名称完成。因此COM并不关心接口的名称或其成员函数的名称。
【7】接口的不变性
接口的不变性是指一旦接口被公布,那么它将永远保持不变。
1、对组件升级时,一般不会修改原有的接口,而是根据需要适当的增加一些新的接口来扩展其原有的功能。
2、通过使用多重接口的继承技术,使得这些组件不但能够支持原有的接口,还可以支持新的接口。
因此多重继承为组件和客户可以智能对同对方的新版本进行交互打下了扎实的基础。
【8】接口的多态性
多态性是指可以按照同一种方式来处理不同的对象。
若两个不同的组件支持同一个接口,那么客户可以使用相同的代码来处理其中任何的一个组件,也就是说,客户可以使用相同的方式来处理不同的组件,从而实现了接口的多态性。
多重接口的支持能力为接口的多态提供了更多的机会,也使得多态的重要性更为突出。因为多态性可以使代码更加有效的被复用。
【9】COM接口内存结构
COM接口的内存结构同C++编译器为抽象基类所生成的内存结构是相同的。
如下纯抽象基类:
interface IX
{
virtual void __stdcall Fx1() = ;
virtual void __stdcall Fx2() = ;
virtual void __stdcall Fx3() = ;
virtual void __stdcall Fx4() = ;
};
如下抽象基类所定义的内存接口示例:
一个纯抽象基类所定义的内存结构分为两部分。
左边是一个指向虚函数表的指针(简称为vtbl指针)
右边是一个虚拟函数表,其中包含一组指向虚拟函数实现的指针。
而指向抽象基类的指针将指向vtbl指针。
而所有的COM接口都必须继承一个名为IUnknown的接口,这意味着所有的COM接口的前三个项都是相同的,其中保存的是IUnknown中三个成员函数实现的地址。
备注:接口的内存结构在不同的操作系统上可能不同。
【10】vtbl指针的作用
vtbl指针在由pIX接口指针到虚拟函数表的过程中,看上去增加了一个额外的级别,有什么作用呢?
(1) C++编译器生成代码时,实现抽象基类的派生类会将特定的实例的信息(如成员数据)同vtbl一起保存。
假设类CA派生于IX,并拥有自己的成员数据m_dA。
单个实例时:pA = new CA;
pA内存结构图如下(实例数据以黑色背景表示):
(2)同一个类的不同实例可以共享同一个vtbl
若我们建立CA两个不同的实例,那么将会有两组不同的实例数据。
但不同的实例可以共享同一vtbl以及相同的实现。
如下代码:
void main()
{
//Create first instance of CA
CA* pA1 = new CA(1.5);
//Create second instance of CA
CA* pA2 = new CA(2.75);
}
同一类的多个实例共享vtbl,如下图:
(3) 多态
代码如下:
#include <iostream>
using namespace std;
#include <objbase.h> interface IX
{
virtual void __stdcall Fx1() = ;
virtual void __stdcall Fx2() = ;
virtual void __stdcall Fx3() = ;
virtual void __stdcall Fx4() = ;
}; class CA : public IX
{
public:
virtual void __stdcall Fx1() { cout << "CA::Fx1" << endl; }
virtual void __stdcall Fx2() { cout << m_dFx2 << endl; }
virtual void __stdcall Fx3() { cout << m_dFx3 << endl; }
virtual void __stdcall Fx4() { cout << m_dFx4 << endl; } CA(double dValue = )
: m_dFx2(dValue * dValue)
, m_dFx3(dValue * dValue * dValue)
, m_dFx4(dValue * dValue * dValue * dValue)
{} private:
double m_dFx2;
double m_dFx3;
double m_dFx4;
}; class CB : public IX
{
public:
virtual void __stdcall Fx1() { cout << "CB::Fx1" << endl; }
virtual void __stdcall Fx2() { cout << "CB::Fx2" << endl; }
virtual void __stdcall Fx3() { cout << "CB::Fx3" << endl; }
virtual void __stdcall Fx4() { cout << "CB::Fx4" << endl; }
}; void Fun(IX* pIx)
{
pIx->Fx1();
pIx->Fx2();
pIx->Fx3();
pIx->Fx4();
} void main()
{
//create instance of CA
CA* pA = new CA();
//create instance of CB
CB* pB = new CB;
//get IX Pointer to CA
IX* pIx = pA;
Fun(pIx);
//get IX Pointer to CB
pIx = pB;
Fun(pIx);
} //Output
/*
CA::Fx1
100
1000
10000
CB::Fx1
CB::Fx2
CB::Fx3
CB::Fx4
*/
通过一个共同的抽象基类来一致的使用两个不同的类,内存结构如下图:
备注:
1、上图的实例数据用空框表示,因为COM一般都不关心实例数据。
2、两个虚拟表的结构相同,即两个表中的函数Fxi所占据的表格项都在第i项。
Good Good Study, Day Day Up.
顺序 选择 循环 总结
COM编程之二 接口的更多相关文章
- [.net 面向对象编程基础] (16) 接口
[.net 面向对象编程基础] (16) 接口 关于“接口”一词,跟我们平常看到的电脑的硬件“接口”意义上是差不多的.拿一台电脑来说,我们从外面,可以看到他的USB接口,COM接口等,那么这些接口的目 ...
- C#编程利器之三:接口(Interface)【转】
C#编程利器之三:接口(Interface) C#接口是一个让很多初学者容易迷糊的东西,用起来好象很简单,定义接口,然后在里面定义方法,通过继承与他的子类来完成具体的实现.但没有真正认识接口的作用的时 ...
- WCF编程系列(二)了解WCF
WCF编程系列(二)了解WCF 面向服务 服务是复用进化的结果,起初的复用是函数,面向对象编程的出现使复用从函数上升到对象,随后面向组件编程又将复用从对象上升到组件,现在面向服务编程将复用 ...
- 转: JavaScript函数式编程(二)
转: JavaScript函数式编程(二) 作者: Stark伟 上一篇文章里我们提到了纯函数的概念,所谓的纯函数就是,对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环 ...
- [Java并发编程(二)] 线程池 FixedThreadPool、CachedThreadPool、ForkJoinPool?为后台任务选择合适的 Java executors
[Java并发编程(二)] 线程池 FixedThreadPool.CachedThreadPool.ForkJoinPool?为后台任务选择合适的 Java executors ... 摘要 Jav ...
- Golang面向API编程-interface(接口)
Golang面向API编程-interface(接口) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Golang并不是一种典型的面向对象编程(Object Oriented Pr ...
- Java-函数式编程(二)Lambda表达式
本文首发:Java-函数式编程(二)Lambda表达式 “Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lamb ...
- Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群
Redis总结(五)缓存雪崩和缓存穿透等问题 前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...
- Pthread 并发编程(二)——自底向上深入理解线程
Pthread 并发编程(二)--自底向上深入理解线程 前言 在本篇文章当中主要给大家介绍线程最基本的组成元素,以及在 pthread 当中给我们提供的一些线程的基本机制,因为很多语言的线程机制就是建 ...
随机推荐
- [转]获得 LayoutInflater 实例的三种方式
转自:http://www.cnblogs.com/androidez/archive/2013/07/01/3164729.html 获得 LayoutInflater 实例的三种方式 在实际开 ...
- cannot open /proc/bus/usb/devices, No such file or directory
由于kernel config中没有打开对应的配置. make menuconfig 选择: Device Drivers ---> [*] USB support ---> [*] US ...
- iOS 获取UUID
-(NSString*)GetUUID { CFUUIDRef puuid = CFUUIDCreate( nil ); CFStringRef uuidString = CFUUIDCreateSt ...
- Shell数组的增删改查
Shell数组的增删改查 shell数组的定义及取值: a=(1 2 3) [root@bogon tmp]# echo ${a[*]} 1 2 3 [root@bogon tmp]# echo $ ...
- opengl 3.3 tutorial
http://www.mbsoftworks.sk/index.php?page=tutorials&series=1
- glOrtho、glFrustum && glPerspective
glOrtho :正交投影,摄像机可以位于裁剪体内,所以near和far可以取两个正值或者一正一负 glFrustum :透视投影,摄像机不可以位于裁剪体内,所以near和fa ...
- 清除Outlook 2013中缓存的邮件地址
1.删除相关文件(可能会没有访问权限): 路径:C:\Documents and Settings\user\用户名\Application Data\Microsoft\Outlook 文件名:ou ...
- 56个睿智帅气貌美的CTO大牛陪你叨逼叨
技术领域门槛高,苦读十年有时不如大牛一指点 可CTO级别的大牛何处找? 别急,别急 他们1月12日都去参加APICloud新品发布会了! (没图说啥子) 各路大牛们云集,不仅是为APICloud新品发 ...
- 在 virtualbox 的 centos7 虚拟机中安装增强工具
在 virtualbox 的 centos7 虚拟机中安装增强工具 centos7 刚刚安装完成时,直接安装 virtualbox 增强工具会出错,需要先把 gcc / kernel-devel / ...
- mysql:查询排名
sql语句查询排名 思路:有点类似循环里面的自增一样,设置一个变量并赋予初始值,循环一次自增加1,从而实现排序: mysql里则是需要先将数据查询出来并先行按照需要排序的字段做好降序desc,或则升序 ...