C++做一个真正动态链接的DLL的做法
来源:互联网 发布:上海交通大学网络教育 编辑:程序博客网 时间:2024/04/28 10:36
让动态链接库真正的动态其实是一件很麻烦的事情。事实上,可以称得上“动态”的函数一共就只有两种,即全局函数和纯虚函数。所以我们有三种做法:
第一种做法,对于接口,不使用任何类,所有导出函数都用extern "C"的全局函数。
第二种做法就是使用COM,COM做了我这篇文章所说的几乎所有的事情,除此之外还顺便把线程同步这些烦人的事情也一并解决了。
第三种做法,导出一个全局函数,让这个函数返回一个纯虚类的接口,然后让这个接口作为工厂创建的别的接口,所有的接口都是纯虚类,不允许使用new来创建对象,也不允许在栈上分配对象。
对于这样的接口类,其构造函数应该是protected的,析构函数则是一个virtual的public函数,构造函数和析构函数的函数体不允许=0,变通的解决办法是简单的加上一个不执行任何代码的{}函数体
此外还有DLL中导出变量的问题。我的观点是旗帜鲜明地反对,更进一步的说,我反对任何DLL中的全局变量和静态变量,不管是不是要导出的。我所认同的正确做法是放到上述所说的放到那个唯一的全局函数所返回的接口的实现类的成员里面。也就是下面代码的ServiceEntry_Impl中。
由于DLL是依赖于文件名的,所以很有可能一个进程中存在很多不同的CRT,而且它们之间的malloc/free,new/delete不能通用,有一个变通的解决方案就是让每一个DLL中的每一个类都用它自己的CRT来管理内存,C++提供给我们一个手段就是重载delete,因为上面已经说了,我们不允许new,接口的构造函数都是protected,所以实际上不用考虑数组分配的问题,只需要重载delete就行了,但是,为了解决导出函数的函数名的问题,这是为了跨编译器,我们的导出函数必须是一个全局函数,所以我们还需要导出一个相当于delete功能的全局函数,然后让我们再写一个基类,让这个基类有一个内联的重载的delete,而这个delete去调用我们导出的那个全局函数。
不要嫌麻烦,事实上,微软自己也没有更好的解决办法,微软的GDI+库的所有导出函数就都是全局函数,然后又把所有的类都写了一个inline的包装去调用那些全局函数。我们这里只不过是包装一个delete而已,别的函数我们用纯虚函数就不用包装了。
最后,别忘了用namespace,这能让你可以放心的用简短的类名而不用担心冲突。
上述这些要求的具体代码类似这样,代码中的#include之类的都被我省略了:
// 在头文件中这样做:
namespace myspace
{
extern "C" XXX_API void DllDelete(void*); //导出的C风格的delete,他的实现就是简单的调用dll中的CRT,这样保证DLL分配的内存由DLL释放
class Allocateable // 所有的接口类都应该从这个类派生,不用考虑菱形继承的问题,Allocateable并没有真的存在任何数据成员
{
public:
operator delete(void* p){DllDelete(p);}
};
class XXX : public Allocateable
{
protected:
XXX(){};
public:
virtual ~XXX(){};
virtual void Foo() = 0;
virtual void Bar() = 0;
};
class ServiceEntry : public Allocateable
{
protected:
ServiceEntry(){};
public:
virtual ~ServiceEntry(){};
virtual XXX* CreateXXX() = 0;
virtual YYY* CreateYYY() = 0;
};
extern "C" XXX_API ServiceEntry* NewServiceEntry(); // 这是第二个导出函数,用户使用所有的功能都得从这里入手。
}
// 在cpp文件里这样做:
namespace myspace
{
void DllDelete(void*p)
{
::operator delete(p);
}
ServiceEntry* NewServiceEntry();
{
return new ServiceEntry_Impl();
}
}
// 此外你还需要写具体的实现:应该放到不同的文件中
// 对于ServiceEntry_Impl,要派生自ServiceEntry并实现那些成员函数
namespace myspace
{
class ServiceEntry_Impl : public ServiceEntry
{
public:
virtual XXX* CreateXXX()
{
return new XXX_Impl();// 类似于ServiceEntry_Impl,XXX_Impl也需要派生自XXX,并且你得实现XXX的接口。
}
virtual YYY* CreateYYY()
{
return new YYY_Impl();// 同上
}
};
}
- C++做一个真正动态链接的DLL的做法
- C++做一个真正动态链接的DLL的做法
- Visual Basic 编译真正的Dll动态链接库文件
- 做一个真正的CMUer
- 做一个真正的程序员!
- 四. 一个简单的DLL(非MFC动态链接库)
- DLL:创建和使用动态链接库的步骤 (C++)
- C#使用C/C++编译的动态链接库dll
- C语言动态链接库DLL的加载
- [c#.net]做tooltip给控件动态添加属性的做法、IExtenderProvider接口的使用
- [c#.net]做tooltip给控件动态添加属性的做法、IExtenderProvider接口的使用
- 动态链接库DLL的链接
- 如何做一个真正的男人
- 什么时候才能做一个真正的程序员?
- 如何做一个真正成熟的人
- 【C/C++开发】C语言 DLL(动态链接库)中申请动态内存释放的问题
- 做最好的自己,做一个真正的奋青
- 做最好的自己,做一个真正的奋青
- ASP调用sql server 存储过程
- Java开发过程中经常碰到数据类型的问题
- JBossTools实践系列:开发标准的JMX MBean服务
- Linux下软件的安装与卸载
- class文件找不到.
- C++做一个真正动态链接的DLL的做法
- Access denied for user 'root'@'localhost' 解决方法
- 没有FLEX的高手露面哪
- 乐观心态
- pystock: 在 Linux 下看股票 (Debian 包)
- 还记得那时候的初恋吗?
- 白话面向智能体编程(Agent Oriented Programmig, AOP)之三
- JS验证出生日期和身份证号
- 白话面向智能体编程(Agent Oriented Programmig, AOP)之四