利用面向对象编程语言开发出来的类组件,相当于过程化语言开发的一个模块,而面向对象用覆盖的方式取代了传统的对模块的直接修改,解决了很多开发中的问题。然而,面向对象的类的代码只能提供源代码级的重用,不能满足跨语言、跨平台的只用要求。例如,用c++开发的类模板无法被其他语言(Delphi,visual basic和powerBuider等)使用。
一个跨语言使用的方案是,把类文件打包成动态链接库(DLL),这样其他的语言就可以调用二进制DLL中的函数。但是,这样简单打包有如下缺陷:一是必须随DLL一起把头文件提交给用户才能用DLL进行开发。二是DLL所封装得函数的修改通常会导致该DLL的客户程序重新编译。
COM(Component Object Model,组件对象模型)是microsoft提出的组件标准,在windows平台上,一个COM通常也是一个DLL动态链接库文件。与常规的DLL类不同,实现COM组件的类需要从一个抽象基类中继承而来,从而拥有一个函数地址表。这样,COM组件的应用客户就可以通过请求的方式查用函数。
实现组件抽象基类又称为接口,COM对于调用者来说,就只能看见接口。COM通过接口向外部提供服务。对于调用者来说,不需要了解组件内部的具体实现,只需要知道接口就可以了。
对于windows下游戏编程最为关系紧密的directx就是基于COM组件的技术的。有人说,要是对directx有一定了解,对于directx基本可以不专门去学。这里介绍COM为了更好的了解dircectx。
最后,通过一个简单的抽象基类的继承来重写c++类,是该c++类接近于COM组件的规范,以帮助了解COM组件的开发应用。
1 直接用c++类的函数:
下面的SimpleMath类有两个函数sum100,sum200,功能是简单的将三个数相加后返回。
新建一个目录:“F:\MathClass”,在内部新建连个文件:SimpleMath.h SimpleMath.cpp。具体代码分别如下:
#ifndef _SimpleMath_Include #define _SimpleMath_Include class SimpleMath { public: int sum100 (int m, int n); int sum200 (int m, int n); }; #endif
#include "SimpleMath.h" int SimpleMath::sum100(int m, int n) { return m+n+100; } int SimpleMath::sum200(int m, int n) { return m+n+200; }
接下来在VS2008中新建win32控制台应用程序。命名为“Compute”,选择空项目。
添加main.cpp文件,对SimpleMath进行调用,并在DOS界面打印结果。具体代码如下:
#include "F:\MathClass\SimpleMath.h" #include "F:\MathClass\SimpleMath.cpp" #include <stdio.h> void main() { SimpleMath *sMath = new SimpleMath(); printf("sum 100 的计算结果为:%d\n", sMath->sum100(50,60)); printf("sum 200 的计算结果为:%d\n", sMath->sum200(50,60)); }
运行程序,显示如下图:
为了将类的代码进行重用,必须提供累的全部实现代码。下面,将类的代码打包到DLL以实现代码的隐藏。
2 将c++类打包成DLL提供调用。
将类打包成DLL的动态链接库,需要将类的函数用_declspec(dllexport)引出。为了清晰说明,这里重新编写程序首先创建DLL:VS2008中,新建win32控制台应用程序,命名为MathDLL,确认->下一步->选中DLL,空项目(如图):
依照上上例中类似,添加SimpleMath.h SimpleMath.cpp,代码分别如下:
#ifndef _SimpleMath_Include #define _SimpleMath_Include class SimpleMath { public: int _declspec(dllexport) sum100 (int m, int n); int _declspec(dllexport) sum200 (int m, int n); }; #endif
#include "SimpleMath.h" int SimpleMath::sum100(int m, int n) { return m+n+100; } int SimpleMath::sum200(int m, int n) { return m+n+200; }
执行程序,之后在Debug或者Release中能找到SimpleMath.libsimpleMath.dll文件
接下来,使用生成的DLL文件,在DOS下调用MathDLL.dll的两个类成员。
VS2008中新建win32应用程序,命名testMathDLL.确认->下一步->选中空项目
在项目中添加main.cpp具体代码如下:
#include "F:\MathClass\SimpleMath.h" //这里只需要把SimpleMath包含进来 #include <stdio.h> void main() { SimpleMath *sMath = new SimpleMath(); printf("sum 100 的计算结果为:%d\n", sMath->sum100(50,60)); printf("sum 200 的计算结果为:%d\n", sMath->sum200(50,60)); }
接下来添加link, 解决方案资源管理器中右键testMathDLL 选择属性,在如图写入
这里值得注意的是,要是把文件MathDLL.lib也放入本程序文件目录中,那么link连接器输入可以是MathDLL.lib
最后把MathDLL.dll放入此应用程序文件内。运行。可以出现和上面例子一样的结果。
3 利用抽象基类改进c++类
抽象基类仅有一些虚函数组成,它的派生类必须提供这些纯虚函数的一个实现。编译器处理一个从抽象基类继承而来的类时,会产生一个虚函数表,该表包含所有在抽象基类或派生类中声明的函数的地址(指针)。
由于每一个虚函数都在虚函数表中有唯一的索引号,这样,调用虚函数的编译处理就可以根据索引号从虚函数表中找到调用函数的地址,从而使用一种更灵活的方式实现函数的调用。可见,抽象基类实际就是COM组件模型中所说的接口,它由一组函数组成,只不过COM有一个关键接口IUnknown,其他接口必须从它继承而来。
COM组件可以简单的看成一个抽象基类的继承类。由于COM组件对象是有COM库加载的,因此,每一个COM组件和接口都有一个全局唯一的标示符(GUID),分别为CLSID 和 IID。当用户需要调用某个COM组件对象的接口方法时,将场地这两个标志为COM库,由COM库在注册表中找到相应的COM 组件的DLL文件,然后加载组件,并返回虚函数表的指针。这样,用户就可以索引出具体调用的函数地址。
下面例子将Simpleath类改造为从ISimpleMath抽象类继承而来,以模仿COM的实现规范。ISimpleMath抽象类仅包含SUM100,SUM200的虚函数的声明,SimpleMath则提供了两个类的实现。最后擦SimpleMath打包成DLL,并编写一个客户程序通过接口调用这个DLL。
按照上面例子中的创建DLL文件的方式,在“F:\MathClass”下创建子目录“Interface”,并在此目录中新建工程“MathDLLWithInterface”。向导中选择DLL 与空项目。点击完成。
在工程中添加ISimpleMath.h头文件,表示创建个抽象基类ISimpleMath。代码如下:
#ifndef _ISimpleMath_Include #define _ISimpleMath_Include class ISimpleMath{ public: virtual int sum100(int m,int n) = 0; virtual int sum200(int m,int n) = 0; }; #endif
再添加SimpleMath头文件和 SimpleMath.cpp实现文件。创建个一个继承与抽象基类的接口ISimpleMath的SimpleMath。代码如下:
#ifndef _SimpleMath_Include #define _SimpleMath_Include #include "ISimpleMath.h" class SimpleMath : public ISimpleMath { public: int _declspec(dllexport) sum100(int m,int n); int _declspec(dllexport) sum200(int m,int n); }; #endif
#include "SimpleMath.h" int SimpleMath::sum100(int m,int n) { return m+n+100; } int SimpleMath::sum200(int m,int n) { return m+n+200; }
执行项目。在Debug文件中可以找到生成了DLL文件和lib文件。
下面创建一个DOS控制台程序。在DLL中调用ISimpleMath接口sum100,sum200。
在VS2008中新建win32控制台应用程序。选择空项目。
按照之前例子添加连接器。输入:F:\MathClass\MathDLLWithInterface\Debug\MathDLLWithInterface.lib
(要是将MathDLLWithInterface.lib文件复制到当前项目的子目录中,那么链接输入就是:MathDLLWithInterface.lib)
新建main.cpp 实现文件,代码如下:
#include "F:\MathClass\MathDLLWithInterface\ISimpleMath.h" #include "F:\MathClass\MathDLLWithInterface\SimpleMath.h" #include <stdio.h> void main() { ISimpleMath *interfaceMath = (ISimpleMath*) new SimpleMath(); printf("sum100的计算结果为:%d\n", interfaceMath->sum100(50,60)); printf("sum200的计算结果为:%d\n", interfaceMath->sum200(50,60)); }
再将上面生成的DLL文件复制到当前项目文件目录中。然后运行main.cpp。
显示如图: