c++中源文件和头文件的区别

上传人:第*** 文档编号:34235010 上传时间:2018-02-22 格式:DOC 页数:5 大小:48KB
返回 下载 相关 举报
c++中源文件和头文件的区别_第1页
第1页 / 共5页
c++中源文件和头文件的区别_第2页
第2页 / 共5页
c++中源文件和头文件的区别_第3页
第3页 / 共5页
c++中源文件和头文件的区别_第4页
第4页 / 共5页
c++中源文件和头文件的区别_第5页
第5页 / 共5页
亲,该文档总共5页,全部预览完了,如果喜欢就下载吧!
资源描述

《c++中源文件和头文件的区别》由会员分享,可在线阅读,更多相关《c++中源文件和头文件的区别(5页珍藏版)》请在金锄头文库上搜索。

1、C+的源代码文件分为两类:头文件(Header file)和源文件(Source code file)。头文件用于存放对类型定义、函数声明、全局变量声明等实体的声明,作为对外接口;而源程序文件存放类型的实现、函数体、全局变量定义.C+的源代码文件分为两类:头文件(Header file)和源文件(Source code file)。头文件用于存放对类型定义、函数声明、全局变量声明等实体的声明,作为对外接口;而源程序文件存放类型的实现、函数体、全局变量定义。对于商业C+程序库,一般把头文件随二进制的库文件发布,而源代码保留。一般情况下头文件常以.h 或.hpp 作为扩展名,而实现文件常以.cpp

2、 或.cc 为扩展名。头文件一般不直接编译,一个源文件代表一个“编译单元”。在在编译一个源文件时,如果引用的类型、函数或其它实体不在本编译单元内,可以通过引用头文件将其它编译单元内实现的实体引入到本编译单元。而从本质上讲,这些源代码文件都是纯文本文件,可以使用任何一款文本编译器进行源代码的编辑,并没有本质的区别,这些头文与实现文件的扩展名只是一种习惯。而 C+的标准库的头文件则不使用扩展名,例如 string、 iostream、cstdio 等头文件。对与源文件也一样,你完全可以使用.inl 或.cplusplus 作为文件的扩展名。事实上,在一些 C+的项目中.inl 被用作源代码文件的扩

3、展名,保存内联函数,直接包含在源文件中,如 ACE(the Adaptive Communication Environment, http:/www.cse.wustl.edu/schmidt/ACE.html)等。gcc 默认支持的 C+源文件扩展名有.cc、.cp、.cpp、.cxx、.c+、.CPP、.C(注意后两项是大写,在Unix/Linux 上的文件名是区分大小写的)。例如在 gcc 中你可以这样编译一个扩展名为.cplusplus 的 C+程序: g+ -x c+ demo.cplusplus 虽然文件名对程序没有任何影响,但.cpp 和.cc 这些扩展名是编译器默认支持的,使

4、用这些扩展名您就不需要手动添加编译选项支持您使用的扩展名,如gcc 中的-x 选项。而实际上,头文件以什么为扩展名并没有什么影响,因为没有人会直接编译头文件,因为头文件里只有声明而没有定义,而在实际的编译过程中,#include预编译指令用到的头文件是被直接插入到源代码文件中再进行编译的,这与直接将头文件的内容复制到#include 行所在的位置是没有区别的,这样就很容易理解#include 可以出现在文件的什么位置,显然放到一个函数体或类的定义里是不合适的。1.1.1. 定义与声明有什么不同一般来讲定义要放在源代码文件中,而声明要放在头文件中。具体哪些内容应该放在源代码文件中,哪些内容应该放

5、在头文件中,需要清楚地理解,哪些是定义,哪些是声明。1.1.1.1. 类的定义与声明类的定义是定义了类的完整结构,包括成员函数与成员变量,如例程2-1。/ 例程 2-1: 类的定义class Pointprivate:int x_;int y_;public:Point( int x, int y);int X( void ) const;int Y( void ) const;而类的声明,只说明存在这一种类型,但并不定义它是什么样的类型,如例程2-2。/ 例程 2-2: 类的声明class Point;类的说明与实现都可以放在头文件中,因为上层代码需要使用 Point 的类必须知道当前工程已

6、经定义了这个类。但应该使用定义还是声明呢?使用声明可以的地方使用定义都是可以的,但是,过多得使用定义会使项目编译时间加长,减慢编译速度,细节可参见(see effective series,item 34)。还有一种情况是必须使用声明的,就是当两个类在定义中出现互相引用的情况时,如例程2-3。当然,这种情况出现的情况比较少,多数情况下也可以通过修改设计尽量避免,在不可避免的情况下只能使用这种方式。/ 例程 2-3: 类定义的交叉引用class B;class A public : B class B public: A* CreateA( void ) const; 类的定义只给出了类包含了哪

7、些数据(成员变量)和接口(成员函数),但并没有给出实现,程序的实现应该放在原代码文件中。如例程2-1中的 Point 类定义在 Point.hpp 头文件中,相应的源代码文件 Point.cpp 的内容如例程2-4所示。/ 例程 2-4: 成员函数的实现Point:Point(int x, inty):x_(x), y_(y)int Point:X( void ) constreturn x_;int Point:Y( void ) constreturn y_;当然,类的成员函数的实现也可以放到头文件中,但编译时默认会为这些函数加上 inline 修饰符,当成内联函数处理。像 Point:X

8、 和 PointY 这样的简单的读值函数,比较适合放到头文件中作为内联函数,详见?inline一节。1.1.1.2. 函数的定义与声明函数的声明只说明函数的外部接口,而不包含函数的实现函数体,如例程2-5所示。/ 例程 2-5: 函数的声明int SplitString(vector& fields, const string& str, const string而函数定义则是包含函数声明和函数体在内的所有部分,如例程2-6所示,给出了一个拆分字符串的函数,虽然效率不高,但它的确是一个能工作的函数。/ 例程 2-6: 函数的定义int SplitString(vector& fields, c

9、onst string& str, const string& delimiters)string tmpstr = str;fields.clear();string:size_type pos1, pos2;for(;) pos1 = pos2 = 0;if(pos1 = tmpstr.find_first_not_of(delimiters, pos2)= string:npos)break;if(pos2 = tmpstr.find_first_of(delimiters, pos1)!= string:npos)fields.push_back(tmpstr.substr(pos1,

10、 pos2 - pos1);else fields.push_back(tmpstr.substr(pos1);break;tmpstr.erase(0, pos2);return fields.size();函数声明可以放在任何一个调用它的函数之前,而且在调用一个函数之前必须在调用者函数之前定义或声明被调函数。函数的定义只能有一次,如果调用者与被调用者不在同一编译单元,只能在调用者之前添加函数的声明。函数定义只能有一次,函数声明可以有无限次(理论上),这也是头文件的作用,将一批函数的声明放入一个头文件中,在任何需要这些函数声明的地方引用该头文件,以便于维护。函数声明之前有一个可选的 exte

11、rn 修饰符,表示该函数是在其它编译单元内定义的,或者在函数库里。虽然它对于函数的声明来讲不是必须的,但可以在一个源文件中直接声明其它编译单元内实现的函数时使用该关键词,从而提高可读性。假如例程2-6中的函数 SplitString 定义在 strutil.cpp 文件中定义,而且在 strutil.cpp 还定义了很多字符串相关的函数,other.cpp 只用到了strutil.cpp 中 SplitString 这一个函数。而您为了提高编译速度,可以直接在 other.cpp 中声明该函数,而不是直接引用头文件,此时最好使用 extern 标识,使程序的可读性更好。1.1.1.3. 变量的

12、定义与声明变量的声明是带有 extern 标识,而且不能初始化;而变量的定义没有 extern标识,可以在定义时初始化,如例程2-7所示。/ 例程 2-7:变量的定义与声明/ 声明extern int global_int;extern std:string global_string ;/ 定义int global_int = 128;std:string global_string = “global string”;在形式上,与函数的声明不同的是,变量的声明中的 extern 是必须的,如果没有 extern 修饰,编译器将当作定义。之所以要区分声明与变量,是在为对于变量定义编译器需要分

13、配内存空间,而对于变量声明则不需要分配内存空间。1.1.1.4. 小结从理论上讲,声明与定义的区别就是:定义描述了内部内容,而声明不表露内部内容,只说明对外接口。例如,类的定义包含了内部成员的声明,而类的声明不包含任何类的内部细节;函数的定义包含了函数体,而函数声明只包括函数的签名;变量的定义可以包含初始化,而变量的声明不可以包含初始化。从语法表现上的共同点,声明可以重复,而定义不可以重复。声明与定义的分离看似有些不方便,但是它可以使实现与接口分离,而且头文件本身就是很好的接口说明文档,具有较好的自描述性,加上现在较智能的集成开发环境(IDE),比起阅读其它类型的文档更方便。C#在 3.0 中

14、也加入了“部分方法(Partial method)”的概念,其作用与头文件基本相似,这也说明了头文件的优点。从工程上讲,头文件的文件名应该与对应的源文件名相同便于维护,如果头文件中包含了多个源文件中的定义或声明,则应该按源文件分组布局头文件中的代码,并且通过注释注明每组所在的源文件。当一个工程的文件较多时应该将源文件与头文件分开目录存放,一般头文件存放在 include 或 inc 目录下,而源文件存放在 source 或 src 目录下,根据经验,一个工程的文件数超过 30 个时应该将源文件与头文件分开存放,当文件较少时直接放到同一目录即可。1.1.2. 头文件中为什么有#ifndef/#d

15、efine/#endif 预编译指令虽然函数、变量的声明都可以重复,所以同一个声明出现多次也不会影响程序的运行,但它会增加编译时间,所以重复引用头文件会使浪费编译时间;而且,当头文件中包含类的定义、模板定义、枚举定义等一些定义时,这些定义是不可以重复的,必须通过一定措施防止重复引用,这就是经常在头文件中看到的#ifndef/#define/#endif 的原因,一般形式如例程2-8 所示。/ 例程2-8#ifndef HEADERFILE_H#define HEADERFILE_H/ place defines and declarations here#endif一些编译器还支持一些编译器指

16、令防止重复引用,例如 Visual C+支持#pragma once指令,而且可以避免读磁盘文件,比#ifndef/endif 效率更高。1.1.3. #include 与#include”filepath”有什么区别在 C+中有两种引用头文件的形式:/ 形式 1#include/ 形式 2#include “filename”其实,C+标准中也没有确定这两种方式搜索文件 filepath 的顺序,而是由编译器的实现确定,其区别就是如果编译器按照第二种形式定义的顺序搜索文件filepath 失败或者不支持这种方式时,将其替换为第一种顺序再进行搜索。而实际上,一般来讲第一种方式都是先搜索编译器的系统目录,而第二种方式则是以被编译的头文件所在目录为当前目录进行搜索,如果搜索失败再在系统头文件里搜索。这两种方式从本质上讲没有什么区别,但当我们自己的程序文件与系统头文件

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 办公文档 > 解决方案

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号