`
tianshibaijia
  • 浏览: 1123087 次
文章分类
社区版块
存档分类
最新评论

为什么有些库的头文件只提供类的预先声明,而无类的定义?

 
阅读更多

1.问题的引出

在程序设计领域,库可以说是一切程序的基础。当今的程序几乎没有一个是从0实现的,或多或少都是建立在已有实现功能模块的基础上,这些可以被程序员使用具有一定功能的代码模块就叫做库。

库的使用方式可粗略分为两种,一是源码级别的使用,二是二进制级别使用。

对于第一种,库的源码对于使用者完全开放,用户不仅可以使用,而且可以理解库的实现原理,甚至可以修改库来扩充功能,总之,源码之前,了无秘密。这种方式的缺点是对于库开发厂商,很难保守自己的知识产权,更别说商业秘密;再就是每次编译程序都需要重新编译库这需要更多的编译连接时间;最后由于人人可以修改,容易导致版本混乱,不便于维系稳定的接口。目前开放源代码的库也有不少,如Qt,GlibC,GTK等等,但大多数不是让用户直接使用源代码,而是提供二进制库的同时附带源代码供用户参考。

对于第二种,库只对用户开放有限的说明,目的是让用户可以使用库,但却不知道库的具体实现。这种库的使用方式,典型的就是提供给用户头文件和.lib二进制文件。头文件通常包含数据结构声明或定义和函数的原型声明以及常量宏定义,而库文件则是实际的数据结构和函数功能实现。(注:对于动态链接的库,只是把链接放到了运行阶段,原理类似)。这种方式下,用户只知道有限的使用信息,对于库的实现对用户是不可见的。这就起到了隐藏实现的作用,对于保护库厂商的知识产权有着良好的支持。毕竟通过逆向工程来分析库的实现是很难的事情。

本文关注的就是能通过第二种使用方式使用的库的实现原理,重点是如何对用户隐藏更多的信息,而不影响其使用。

2.需要隐藏的信息

库对于用户来说就是(包含数据结构定义和函数声明的)头文件 +(带导出符号的)二进制实现文件。

所以发生隐藏的地方也就只有两处:头文件,二进制文件。

对于需要隐藏的内容主要是:数据结构的定义,函数的名称(或声明),函数实现。

所谓的隐藏,指的是源代码中符号名,数据结构和算法实现的隐藏。

1)对于函数的实现。都已经编译成二进制,所以其源代码已经自动隐藏了,在库中再也找不到实现这些运算的源代码的痕迹了。在头文件中更不可能出现函数的实现代码。

2)对于函数的名字与原型。二进制库中不存在原型的声明,只有在导出表中存在函数名符号的可能。头文件中可能有函数的声明。这里有个依赖的问题。那就是二进制文件导出符号表中存在函数的名称是头文件存在函数原型声明的前提。正常做法是导出表中存在的函数名中只有一部分在头文件中进行了声明,未被声明的部分,主要用于库厂商自己开发使用,用户无法直接使用。这在WindowsAPI库中普遍存在,微软也因此遭到众多人的谴责,因为微软可以使用比其他人更多的库函数。Vega的库也是一样,尽管库的导出符号表中包含了大量的函数,包括C++成员函数,C函数,然而其提供给用户的头文件中并没有包含某些C函数的声明和大部分C++类的定义(注意:C++成员函数不能单独声明,必须通过整个类的定义声明)。因此,用户只能使用有限的C函数进行开发,而不能使用C++的接口进行开发。VegaPrime显然显得更加开放,它给用户提供了大量的C++类接口使用,更加暴露了其库的数据结构类型。

如果头文件中声明了函数,而库导出表中没有响应的符号,则此函数根本无法使用,因为在连接时会发生找不到函数的错误。

3)对于数据结构的定义。数据结构在C中主要指结构体,共同体等,在C++中主要是类。是否把这些数据结构的定义开放给用户,取决于库厂商。如果允许用户直接对数据结构进行操作,比如为其成员赋值,调用其成员函数,则必须把其定义开放给用户。因为编译器在进行此类操作的编译时,必须要知道数据结构的内存布局,而内存布局只有通过其定义得知。这方面典型的库是MFCMFC允许用户直接使用其类建立对象,并调用其成员函数。因此其提供给用户的头文件中存在大量的类的定义。

然而如果不允许用户直接对数据结构进行操作,而是提供一组对数据结构进行操作的函数,就可以对用户隐藏数据结构的定义。在提供给用户的头文件中不需要包含数据结构的定义,只需要提供预先声明就行,如: class ClibClass; struct STUDENT; 在操作这些数据结构的函数的参数中,不能出现数据结构的类型,因为如果出现的话,编译器需要根据数据结构的内存布局进行压栈操作,取而代之,应该使用数据结构的指针类型作为参数类型。因为编译器对指针参数在压栈操作时,无论什么类型的指针都具有相同的字节数(如4字节)。至于指针类型的解释,都放在函数实现中完成了。原则上可以在函数声明中声明指针参数时,可以以任何类型。因为在函数实现中对它进行类型转换就行了。但是对于C++却不行,原因是C++编译后的函数名字与参数类型有关,如果函数声明和函数定义时使用不同类型的指针类型,那么两者编译后的函数名是不相同的,所以也就在链接时找不到函数名。对于C语言,编译后的函数名与参数类型无关,因此函数声明中的参数类型可以与实现时参数类型不一致。也就是说在预先声明结构体时,可以采取任何名字,如头文件:

struct SanyName;

extern FuntionLib(struct SanyName * p);

而实现文件:

Struct Student

{

Int age;

Double score;

}

FuntionLib(struct Student *p) //指针类型自动转换

{

p->age = 20;

p->score = 88.5;

}

这样做不仅可以隐藏数据结构的实现,而且连数据结构的真实名字也隐藏了。当然这样做没什么必要。

3.vega头文件为啥只有class vgWindow;而无vgWindow的定义?

相信使用过vega库的读者,都有过类似的疑惑:vg.h头文件中只有类似class vgWindow;的预先类声明,却不见vgWindow类的定义。读者也就不知道vgWindow的成员有什么。相信现在你已经明白了,因为使用vega库根部不需要(或者不想)让用户知道类的内部结构,由于类是C++里的概念,所以这里预先声明的类的名称应该与vega库中的一致,这里class vgWindow;的唯一作用也就是使得编译器编译函数时使得函数的修饰名字与库的导出符号一致。

分享到:
评论

相关推荐

    C++标准程序库STL的架构

    5.9.2 预先定义的仿函数 35 5.10 容器内的元素<class T> 36 5.10.1 容器元素的条件 36 5.10.2 value和reference 37 5.11 STL内部的错误处理和异常处理 37 5.11.1 错误处理(Error Handling) 37 5.11.2 异常处理 38 ...

    C++高级参考手册 完全自学 内容详细 讲解通俗易懂

    1.2 为什么C++会成功 1.2.1 较好的C 1.2.2 采用渐进的学习方式 1.2.3 运行效率 1.2.4 系统更容易表达和理解 1.2.5 “库”使你事半功倍 1.2.6 错误处理 1.2.7 大程序设计 1.3 方法学介绍 1.3.1 复杂性 ...

    C++编程思想 例子和叙述讲解C++编程

    1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐进的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 1.3 方法学介绍 5 1.3.1 ...

    C++编程思想 PDF

    1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐进的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 1.3 方法学介绍 5 1.3.1 ...

    C++编程思想.pdf

    1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐进的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 1.3 方法学介绍 5 1.3.1 ...

    C++编程思想

    1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐进的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 1.3 方法学介绍 5 1.3.1 ...

    C++编程思想.rar

    1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐进的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 1.3 方法学介绍 5 1.3.1 ...

    c++ 编程思想 (高清pdf)

    1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐进的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 1.3 方法学介绍 5 1.3.1 ...

    C++编程思想(程序员必读)

    1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐进的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 1.3 方法学介绍 5 1.3.1 ...

    C++编程思想PDF版

    1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐进的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 ...

    C++高级参考手册(供高级程序员参考)

    1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐进的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 1.3 方法学介绍 5 1.3.1 ...

    C++编程思想,很经典

    1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐进的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 1.3 方法学介绍 5 1.3.1 ...

    C++编程思想 编程

    1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐进的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 1.3 方法学介绍 5 1.3.1 ...

    C++编程思想(用简单的例子和简练的叙述讲解C++编程)

    1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐进的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 1.3 方法学介绍 5 1.3.1 ...

    C++编程思想16-21.rar (完结)

    1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐进的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 1.3 方法学介绍 5 1.3.1 ...

Global site tag (gtag.js) - Google Analytics