在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
导出类设计上一篇博客详细陈述了类库开发的各个知识点(http://blog.csdn.net/z702143700/article/details/45989993),本文将进一步陈述,对于类库开发过程中导出类的开发规范问题。 C/C++开发的DLL当初是作为函数级共享库设计的,并不能真正提供一个类所必需的信息。类层上的程序复用只有Java和C#生成的类文件才能做到。所以,当我们在用C++开发类库时经常会遇到类的设计问题,设计不好,就会出现DLL地狱。 DLL地狱导出类的DLL在维护和修改时如果增加成员变量、修改导出类的基类等操作都可能导致意想不到的后果,也许用户更新了最新版本的DLL库后,应用程序就再也不能工作了。即著名的DLL Hell(DLL地狱)问题。 DLL地狱问题的产生
举个例子:假设DLL有一个导出类ClassTool:
应用程序使用该类:
由于需求改变需要给类加一个变量:
把更新后的DLL编译连接完成后,复制到应用程序目录,这个倒霉的应用程序调用GetVar方法后,恐怕再也无法得正确的值了。这样的事情,称它是个地狱(Hell)。 分析: 然后,当调用“d.GetVar()”时,把申请的这一块内存做为this指针传给GetVar函数,GetVar函数从this指向的位置开始, 加上m_var1应有的偏移量,计算m_var1所在的内存位置,并从该位置取数据返回。m_var1相对this的偏移量是由m_var1在类中定义的位置决定的,定义在前的成员变量在内存中也更靠前。这个偏移量在DLL编译时确定。 当ClassTool 的定义改为修改后的状态时,有些东西变了。 第一个变的是内存的大小。因为修改后的ClassTool 多了一个成员变量,所以内存也变大了。然而这一点应用程序并不知道。 第二个变的是m_var1的偏移地址。因为在m_var1之前定义了一个m_var2,m_var1的实现偏移地址实际已经靠后了。所以d.GetVar()访问的将是原来m_var1后面的那个位置,而这个位置已经超出原来那块内存的后部范围了。 很显然,在更换了DLL后,应用程序还按原来的大小申请了一块内存,而它调用的方法却访问了比这块内存更大的区域,出错再在所难免。 总言而之,一不小心,你的程序就会掉进地狱。通过对这些引起出错的情况进行分析,会发现其实只有三点变化会引起出错,因为这三点是使用这个DLL的应用程序在编译时就需要确定的内容,它们分别是: 导出类设计规范在进行导出类设计时就应该遵循以下默认的规范: 2.不直接访问成员变量。应用程序直接访问类的成员变量时会用到该变量的偏移地址。所以避免偏移地址依赖的办法就是不要直接访问成员变量。把所有的成员变量的访问控制都定义为保护型(protected)以上的级别,并为需要访问的成员变量定义Get或Set方法。Get或Set方法在编译新DLL时会被重新编译,所以总能访问到正确的变量位置。 3.尽量不使用虚函数。就算有也不要让应用程序直接访问它。因为类的构造函数已经是私有(privated)的了,所以应用程序也不会去继承这个类,也不会实现自己的多态。如果导出类的父类中有虚函数,或设计需要(如类工厂模式之类的框架),一定要把这些函数声明为保护的(protected)以上的级别, 并为应用程序重新设计调用该虑函数的成员函数。这一点也类似于对成员变量的处理。 建议,你在发布导出类的DLL的时候,重新定义一个类的声明,这个声明可以不管原来的类里的成员变量之类的,只把接口函数列在类的声明里,如下面的例子:
在应用程序中,使用ClassInterface作为类的头文件,这样可以避免任何可能导致的安全问题。 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论