C++ COM编程之什么是接口?_C 语言

什么是接口?

说到COM,就不得不说接口了;在进行COM开发的过程中,可以说,我一直都在和各种各样的接口打交道。那接口是什么?对于COM来说,接口是一个包含一个函数指针数组的内存结构,每一个数组元素包含的是一个由组件所实现的函数的地址;所以,对于COM,接口就是这样的一个内存结构,其它东西都是一些COM并不关心的实现细节。

在C++中,可以使用抽象基类来实现COM接口。由于一个COM组件可以支持任意数目的接口,因此对于组件,可以使用抽象基类的多重继承来实现它。

接口的好处

接口提供了两个不同对象间的一种连接。对于客户来说,一个组件就是一个接口集。客户只能通过接口才能同COM组件打交道。而整体上来讲,客户对于一个组件可以说是知之甚少;甚至在某些时候,客户甚至不必知道一个组件所提供的所有接口,就像你进行Windows  Shell开发时,对于一个它提供的组件,很多时候,你不可能知道所有的接口的。对于一个应用程序而言,接口是最重要的。组件本身只不过是接口的实现细节。

在实际开发时,你并不需要去理会组件的实现细节,你面对的是接口,面对接口工作。即使组件的开发者将组件的实现替换掉了,而接口不变,你的程序也不需要变动。接口,就像一个标准一样,让我们去遵从这个标准。之前做的一个项目就是替换一个组件的实现层,而对于接口,则不需要进行变更。

简单的实现

通过一个简单的例子来理解接口:

复制代码 代码如下:

/*
** FileName     : SimpleInterfaceDemo
** Author       : Jelly Young
** Date         : 2013/12/11
** Description  : More information, please go to http://www.jb51.net
*/
#include <iostream>
#include <combaseapi.h>
using namespace std;
interface IExample1
{
     virtual void __stdcall Fx1() = 0;
     virtual void __stdcall Fx2() = 0;
};
interface IExample2
{
     virtual void __stdcall Fy1() = 0;
     virtual void __stdcall Fy2() = 0;
};
// Implementation
class CImplementation : public IExample1, public IExample2
{
public:
     // Implementation IExample1
     void __stdcall Fx1() { cout<<"CImplementation::Fx1"<<endl; }
     void __stdcall Fx2() { cout<<"CImplementation::Fx2"<<endl; }
     // Implementation IExample2
     void __stdcall Fy1() { cout<<"CImplementation::Fy1"<<endl; }
     void __stdcall Fy2() { cout<<"CImplementation::Fy2"<<endl; }
};
// Client
int main()
{
     cout<<"Create an instance of the component."<<endl;
     CImplementation *pCImplementation = new CImplementation;
     // Get the IExample1 pointer
     IExample1 *pIExample1 = pCImplementation;
     // Use the IExample1 interface
     pIExample1->Fx1();
     pIExample1->Fx2();
     // Get the IExample2 pointer
     IExample2 *pIExample2 = pCImplementation;
     // Use the IExample2 pointer
     // Use the IExample2 interface
     pIExample2->Fy1();
     pIExample2->Fy2();
     // Destroy the component
     if (pCImplementation != NULL)
     {
          delete pCImplementation;
          pCImplementation = NULL;
          pIExample1 = NULL;
          pIExample2 = NULL;
     }
}

上面的例子中,client通过两个接口pIExample1和pIExample2来和组件进行通信。在声明接口时,使用了两个纯抽象基类IX和IY。总结上面代码的关键之处在于:

1.COM接口在C++中是用纯抽象基类实现的;
2.一个COM组件可以提供多个接口;
3.一个C++类可以使用多继承来实现一个可以提供多个接口的组件。

细节剖析

interface这货是从哪里来的?你会很好奇,是不是好奇的连下巴都掉下来了?C++也有interface关键字?不错,这个关键字是在combaseapi.h头文件中定义的,定义如下:

复制代码 代码如下:

#define __STRUCT__ struct
#define interface __STRUCT__

说白了,就是用C++的关键字struct定义的一个结构体。使用struct定义有什么好处呢?首先需要搞清楚struct和class的区别。学了Java和C#的都知道,由于接口中定义的都是允许客户调用的,所以在接口中就不需要private和protected的了,如果使用class,而必须还要使用public关键字强调接口的公有属性,而struct默认的都是公有属性,这样就省去了添加public关键字的麻烦。

__stdcall是什么?__stdcall是一种用来修饰函数的关键字,主要约定了两件事情:

1.参数传递顺序,__stdcall表示参数从右向左压入堆栈;
2.调用堆栈由谁(调用函数或被调用函数)清理,__stdcall表示由被调用函数修改堆栈。

接口是由纯虚函数实现的,为什么是要这样?以及展现出来的多态,这个说来话长,我将在下一篇博文中进行总结。

总结

在这里对接口基础知识进行了扫盲式的总结,而这些简单的知识点也是日后开发中会经常遇到的,这里把这些东西掌握好了,等日后开发时也会感到很轻松。希望大家能从这篇博文中学到一定的知识,同时也希望大家对我的博客提一些中肯的建议。

时间: 2024-12-20 16:08:46

C++ COM编程之什么是接口?_C 语言的相关文章

详解state状态模式及在C++设计模式编程中的使用实例_C 语言

每个人.事物在不同的状态下会有不同表现(动作),而一个状态又会在不同的表现下转移到下一个不同的状态(State).最简单的一个生活中的例子就是:地铁入口处,如果你放入正确的地铁票,门就会打开让你通过.在出口处也是验票,如果正确你就可以 ok,否则就不让你通过(如果你动作野蛮,或许会有报警(Alarm),:)). 有限状态自动机(FSM)也是一个典型的状态不同,对输入有不同的响应(状态转移). 通常我们在实现这类系统会使用到很多的 Switch/Case 语句,Case 某种状态,发生什么动作,C

解析C++编程中的bad_cast异常_C 语言

由于强制转换为引用类型失败,dynamic_cast 运算符引发 bad_cast 异常.语法 catch (bad_cast) statement 备注 bad_cast 的接口为: class bad_cast : public exception { public: bad_cast(const char * _Message = "bad cast"); bad_cast(const bad_cast &); virtual ~bad_cast(); }; 以下代码包含

深入解析C++编程中类的封装特性_C 语言

共用接口和私有实现的分离 C++通过类来实现封装性,把数据和与这些数据有关的操作封装在一个类中,或者说,类的作用是把数据和算法封装在用户声明的抽象数据类型中. 实际上用户往往并不关心类的内部是如何实现的,而只需知道调用哪个函数会得到什么结果,能实现什么功能即可. 在声明了一个类以后,用户主要是通过调用公用的成员函数来实现类提供的功能(例如对数据成员设置值,显示数据成员的值,对数据进行加工等).因此,公用成员函数是用户使用类的公用接口(public interface),或者说是类的对外接口. 类

解读C++编程的相关文件操作_C 语言

C++文件的概念 迄今为止,我们讨论的输入输出是以系统指定的标准设备(输入设备为键盘,输出设备为显示器)为对象的.在实际应用中,常以磁盘文件作为对象.即从磁盘文件读取数据,将数据输出到磁盘文件.磁盘是计算机的外部存储器,它能够长期保留信息,能读能写,可以刷新重写,方便携带,因而得到广泛使用. 文件(file)是程序设计中一个重要的概念.所谓"文件",一般指存储在外部介质上数据的集合.一批数据是以文件的形式存放在外部介质(如磁盘.光盘和U盘)上的.操 作系统是以文件为单位对数据进行管理的

详解C++编程中的虚函数_C 语言

我们知道,在同一类中是不能定义两个名字相同.参数个数和类型都相同的函数的,否则就是"重复定义".但是在类的继承层次结构中,在不同的层次中可以出现名字相同.参数个数和类型都相同而功能不同的函数. 人们提出这样的设想,能否用同一个调用形式,既能调用派生类又能调用基类的同名函数.在程序中不是通过不同的对象名去调用不同派生层次中的同名函数,而是通过指针调用它们.例如,用同一个语句"pt->display( );"可以调用不同派生层次中的display函数,只需在调用前

简介C++编程中的运算符重载_C 语言

所谓重载,就是重新赋予新的含义.函数重载就是对一个已有的函数赋予新的含义,使之实现新功能,因此,一个函数名就可以用来代表不同功能的函数,也就是"一名多用". 运算符也可以重载.实际上,我们已经在不知不觉之中使用了运算符重载.例如,大 家都已习惯于用加法运算符"+"对整数.单精度数和双精度数进行加法运算,如5+8, 5.8 +3.67等,其实计算机对整数.单精度数和双精度数的加法操作过程是很不相同的, 但由于C++已经对运算符"+"进行了重载,所以

深入解析C++编程中的静态成员函数_C 语言

C++静态成员函数 与数据成员类似,成员函数也可以定义为静态的,在类中声明函数的前面加static就成了静态成员函数.如 static int volume( ); 和静态数据成员一样,静态成员函数是类的一部分,而不是对象的一部分. 如果要在类外调用公用的静态成员函数,要用类名和域运算符"::".如 Box::volume( ); 实际上也允许通过对象名调用静态成员函数,如 a.volume( ); 但这并不意味着此函数是属于对象a的,而只是用a的类型而已. 与静态数据成员不同,静态成

Mac OS X 10.8 中编译APUE(Unix环境高级编程)的源代码过程_C 语言

最近在温习APUE(<unix环境高级编程>),以前都是在linux下搞,现在打算在自己机器弄下,于是google了下,把编译的事情搞定了,修改了一些教程的一些错误,比如下载链接之类的. 1.下载源文件,我这里是第二版,貌似第三版的英文版出来了... 复制代码 代码如下: wget http://www.apuebook.com/src.2e.tar.gz 2.解压 复制代码 代码如下: tar zxf src.2e.tar.gz 3.修改些东西 复制代码 代码如下: cd apue.2e/

c语言网络编程-标准步骤(比较简单)_C 语言

c语言网络编程-标准步骤,真的很简单啊 server.c 复制代码 代码如下: #include <stdio.h>#include <stdlib.h>#include <string.h>#include <netdb.h>#include <netinet/in.h>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h> #d

MySQL的C语言API接口_C 语言

1.首先当然是连接数据库,函数原型如下: MYSQL * STDCALL mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag); 第一个参数 MYSQL是 C api中一个非常重要的变量,里面内存非常丰富