DLL 导出类的问题

使用DLL的一个比较严重的问题就是编译器之间的兼容性问题。不同的编译器对c++函数在二进制级别的实现方式是不同的。所以对基于C++的DLL,如果编译器 不同就有很麻烦的。如果创建的是MFC扩展DLL,就不会存在问题,因为它只能被动态连接到MFC的客户应用程序。这里不是本文讨论的重点。

一、重新编译问题

我们先来看一个在实际中可能遇到的问题:

比如现在建立好了一个DLL导出了CMyClass类,客户也能正常使用这个DLL,假设CMyClass对象的大小为30字节。如果我们需要修改DLL 中的CMyClass类,让它有相同的函数和成员变量,但是给增加了一个私有的成员变量int类型,现在CMyClass对象的大小就是34字节了。当直接把这 个新的DLL给客户使用替换掉原来30字节大小的DLL,客户应用程序期望的是30字节大小的对象,而现在却变成了一个34字节大小的对象,糟糕,客户程序出错 了。

类似的问题,如果不是导出CMyClass类,而在导出的函数中使用了CMyClass,改变对象的大小仍然会有问题的。这个时候修改这个问题的唯一办法就是替 换客户程序中的CMyClass的头文件,全部重新编译整个应用程序,让客户程序使用大小为34字节的对象。

这就是一个严重的问题,有的时候如果没有客户程序的源代码,那么我们就不能使用这个新的DLL了。

二、解决方法

为了能避免重新编译客户程序,这里介绍两个方法:(1)使用接口类。(2)使用创建和销毁类的静态函数。

1、使用接口类

 使用接口类也就是创建第二个类,它作为要导出类的接口,所以在导出类改变时,也不需要重新编译客户程序,因为接口类没有发生变化。

假设导出的CMyClass类有两个函数FunctionA和FunctionB。现在创建一个接口类CMyInterface,下面就是在DLL中的CMyInterface类的头文件的代码:

 

  1. #include "MyClass.h"   
  2. class _declspec(dllexport) CMyInterface   
  3. {   
  4.     CMyClass *pmyclass;   
  5.     CMyInterface();   
  6.     ~CMyInterface();   
  7. public:   
  8.     int FunctionA(int);   
  9.     int FunctionB(int);   
  10. };  

 

而在客户程序中的头文件稍不同,不需要INCLUDE语句,因为客户程序没有它的拷贝。相反,使用一个CMyClass的向前声明,即使没有头文件也能编译:

 

  1. class _declspec(dllexport) CMyInterface  
  2. {  
  3.     class CMyClass;//向前声明  
  4.     CMyClass *pmyclass;  
  5.     CMyInterface();  
  6.     ~CMyInterface();  
  7. public:  
  8.     int FunctionA(int);  
  9.     int FunctionB(int);  
  10. };  

 

在DLL中的CMyInterface的实现如下:

 

  1. CMyInterface::CMyInterface()  
  2. {  
  3.     pmyclass = new CMyClass();  
  4. }  
  5. CMyInterface::~CMyInterface()  
  6. {  
  7.     delete pmyclass;  
  8. }  
  9. int CMyInterface::FunctionA()  
  10. {  
  11.     return pmyclass->FunctionA();  
  12. }  
  13. int CMyInterface::FunctionB()  
  14. {  
  15.     return pmyclass->FunctionB();      
  16. }  
  17. //.....  

 

对导出类CMyClass的每个成员函数,CMyInterface类都提供自己的对应的函数。客户程序与CMyClass没有联系,这样任意改CMyClass也不会有问题,因为CMyInterface类的大小没有发生变化。即使为了能访问CMyClass中的新增变量而给CMyInterface类加了函数也不会有问题的。

但是这种方法也存在明显的问题,对导出类的每个函数和成员变量都要对应实现,有的时候这个接口类会很庞大。同时增加了客户程序调用所需要的时间。增加了程序的开销。

2、使用静态函数

还可以使用静态函数来创建和销毁类对象。创建一个导出类的时候,增加两个静态的公有函数CreateMe()/DestroyMe(),头文件如下:

 

  1. class _declspec(dllexport) CMyClass  
  2. {  
  3.      CMyClass();  
  4.      ~CMyClass();  
  5. public:  
  6.      static CMyClass *CreateMe();  
  7.      static void DestroyMe(CMyClass *ptr);  
  8. };  

 

实现函数就是:

 

  1. CMyClass * CMyClass::CMyClass()  
  2. {  
  3.       return new CMyClass;  
  4. }  
  5. void CMyClass::DestroyMe(CMyClass *ptr)  
  6. {  
  7.       delete ptr;  
  8. }  

 

然后象其他类一样导出CMyClass类,这个时候在客户程序中使用这个类的方法稍有不同了。如若想创建一个CMyClass对象,就应该是:

 

  1. CMyClass x;  
  2. CMyClass *ptr = CMyClass::CreateMe();  

 

在使用完后删除:

 

  1. CMyClass::DestroyMe(ptr);  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值