【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编程之二 接口的更多相关文章

  1. [.net 面向对象编程基础] (16) 接口

    [.net 面向对象编程基础] (16) 接口 关于“接口”一词,跟我们平常看到的电脑的硬件“接口”意义上是差不多的.拿一台电脑来说,我们从外面,可以看到他的USB接口,COM接口等,那么这些接口的目 ...

  2. C#编程利器之三:接口(Interface)【转】

    C#编程利器之三:接口(Interface) C#接口是一个让很多初学者容易迷糊的东西,用起来好象很简单,定义接口,然后在里面定义方法,通过继承与他的子类来完成具体的实现.但没有真正认识接口的作用的时 ...

  3. WCF编程系列(二)了解WCF

    WCF编程系列(二)了解WCF   面向服务     服务是复用进化的结果,起初的复用是函数,面向对象编程的出现使复用从函数上升到对象,随后面向组件编程又将复用从对象上升到组件,现在面向服务编程将复用 ...

  4. 转: JavaScript函数式编程(二)

    转: JavaScript函数式编程(二) 作者: Stark伟 上一篇文章里我们提到了纯函数的概念,所谓的纯函数就是,对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环 ...

  5. [Java并发编程(二)] 线程池 FixedThreadPool、CachedThreadPool、ForkJoinPool?为后台任务选择合适的 Java executors

    [Java并发编程(二)] 线程池 FixedThreadPool.CachedThreadPool.ForkJoinPool?为后台任务选择合适的 Java executors ... 摘要 Jav ...

  6. Golang面向API编程-interface(接口)

    Golang面向API编程-interface(接口) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Golang并不是一种典型的面向对象编程(Object Oriented Pr ...

  7. Java-函数式编程(二)Lambda表达式

    本文首发:Java-函数式编程(二)Lambda表达式 “Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lamb ...

  8. Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群

    Redis总结(五)缓存雪崩和缓存穿透等问题   前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...

  9. Pthread 并发编程(二)——自底向上深入理解线程

    Pthread 并发编程(二)--自底向上深入理解线程 前言 在本篇文章当中主要给大家介绍线程最基本的组成元素,以及在 pthread 当中给我们提供的一些线程的基本机制,因为很多语言的线程机制就是建 ...

随机推荐

  1. ASP.NET MVC 中将数据从View传递到控制器中的表单提交法

    本方法以搜索功能为例,在view中输入要搜索的关键字,提交到相应controller中进行处理. view中代码: <div class="searchBox"> @u ...

  2. Flash Socket简单调试工具

    写了一个简单的Flash Socket调试工具,可用来简单调试本地或者外部socket服务器,使用的时候注意Flash socket的安全策略问题,有问题请联系sky-wang@qq.com.

  3. python 递归

    学习python,正好用一个例子练习一下递归. 参考文档: http://www.runoob.com/python/python-exercise-example18.html 题目:求s=a+aa ...

  4. 一个短小的JS函数,用来得到仅仅包含不重复元素的数组

    下面函数主要利用了数组的sort方法,之后的逻辑是看最后一个元素是否等于要添加的元素,如果不是就往尾后加. 这个做法的效率等于sort方法的效率,还过得去. 代码: <!DOCTYPE HTML ...

  5. Docker镜像的创建、存出、载入

    创建镜像的方法有三种:基于已有镜像的容器创建.基于本地模板导入.基于Dockerfile创建,本博文讲解前两种. 基于已有镜像的容器创建 该方法是使用docker commit命令,其命令格式为:   ...

  6. Android Platform Guide

    This guide shows how to set up your SDK environment to deploy Cordova apps for Android devices, and ...

  7. 第一段nodejs代码

    步骤一.创建服务器 接下来我们使用 http.createServer() 方法创建服务器,并使用 listen 方法绑定 8888 端口. 函数通过 request, response 参数来接收和 ...

  8. Can't find keyplane that supports type 4 for keyboard iPhone-Portrait-NumberPad; using 3876877096_Portrait_iPhone-Simple-Pad_Default

    在Xcode6下的模拟器点击UITextField控件显示键盘时会显示如下错误提示: Can’t find keyplane that supports type 4 for keyboard iPh ...

  9. sql server 还原数据库时提示数据库正在使用,无法进行操作的解决方法

    这个问题的原因在于有用户连接了当前要做还原的数据库,这里的用户甚至包括当前要做还原的用户.解决办法就是关闭与要还原数据库的所有连接. 脚本之家小编推荐的一个方法:到服务里面重启下sqlserver服务 ...

  10. SQL Server 2008 Express 安装或卸载时提示“重启计算机失败"的解决办法

    安装或卸载SQL Server 遇到错误提示:以前的某个程序安装已在安装计算机上创建挂起的文件操作.运行安装程序之前必须重新启动计算机.如下图: 解决办法: 1.在开始->运行中输入regedi ...