C++中头文件的概念与基本编写方法
1标准库中的头文件
C++标准库中的一切内容都被放在名字空间std中(名字空间中的内容对外是不可见的),但是带来了一个新问题,无数现有的C++代码都依赖于使用了多年的伪标准库中的功能,如声明在<iostream.h>等头文件中的功能,使用std包装标准库导致现有代码的不可用,为了兼容这种情况,标准委员会为包装了std的那部分标准库创建了新的头文件,新的头文件的文件名与旧的一样,只是没有.h这个后缀,如<iostream.h>就变成了<iostream>。对于C头文件,采用同样的方法,但还在每个头文件名前加了字符c,如<string.h>就变成了<cstring>,<stdio.h>变成了<cstdio>。最好使用新的文件头,使用新的文件头的C++程序,需要使用usingnamespacestd或者usingnamespacestd::指定的类名,等方法来使需要的类对于我们的代码可视。
2自定义的头文件
为了防止头文件被重复引用,最好使用预处理定义,如下所示:
#ifndefMYHEAD_H #defineMYHEAD_H ……//头文件中的内容 #endif
(1)#ifndef:
指示符#ifndef用来检查头文件的内容是否在前面已经被定义过,如果定义过,则#ifndef与#endif之间的语句将不被执行.所以习惯上要把头文件的定义写在这两个语句之间.
如:对于MYHEAD.H这个头文件
#ifndefMYHEAD_H #defineMYHEAD_H #include"myhead.h" ...... #endif
(2)#ifdef
指示符#ifdef常常被用来判断一个预处理器常量是否已被定义,以便有条件地包含程序代码。
如:
intmain() { #ifdefDEBUG cout<<"Beginningexecutionofmain()\n"; #endif stringword; vector<string>text; while(cin>>word) { #ifdefDEBUG cout<<"wordread:"<<word<<"\n"; #endif text.push_back(word); } //.....}
在此程序中,如果定义了DEBUG,则其中包含的两个语句都将被执行,如果没有定义,则其中的两个输出语句不被执行。
3预处理相关知识
(1)#ifdef:判断一个预处理常量是否被定义,如#infefDEGUG
(2)#ifndef:判断一个预处理常量是否没被定义
(3)#define:定义一个预处理常量,如#defineDEBUG
(4)#include
(5)#endif
(6)对预处理常量的定义还可以在编译时进行,如CC–DDEBUGmain.c
(7)编译C++程序时,编译器自动定义了一个预处理器名字__cplusplus(注意前面有两个下划线),因此可以根据这个来判断该程序是否是C++程序,以便有条件地包含一些代码,如:
#ifndefMYHEAD_H #defineMYHEAD_H #ifdef__cplusplus extern"C"{ #endif intDMpostprocessing(); #ifdef__cplusplus } #endif #endif
(8)在编译C程序时,编译器会自动定义预处理常量__STDC__。当然__cplusplus和__STDC__不会同时被定义;
(9)另外两个比较有用的预定义常量是__LINE__(记录文件已经被编译的行数)和__FILE__(包含正在被编译的文件名称)。使用如下:
if(element_count==0) cerr<<"Error:"<<__FILE__ <<":line"<<__LINE__ <<"element_countmustbenon-zero.\n";
(10)__DATE__:编译日期,当前被编译文件的编译日期
(11)__TIME__:编译时间,当前被编译文件的编译时间
格式如:hh:mm:ss
08:17:05 Oct312006
(12)C库头文件的C++名字总是以字母C开头,后面去掉.h,如assert.h在C++中为cassert;
assert()是C语言标准库中提供的一个通用预处理器宏。常用其来判断一个必需的前提条件,以便程序能够正确执行。与其关联的头文件是:#include<assert.h>
如:
assert(filename!=0);
表示:如果后面的程序能够正确执行,需要filename不为0,如是条件为假,即其等于0,断言失败,则程序将输出诊断消息,然后终止。
其c++名字是:cassert
C库头文件的C++名字总是以字母C开头
注:在C++中使用C标准库中的头文件时,一定要使用usingnamespacestd;来使其处在一个名字空间中,才能正确使用
(13)在C++中头文件后缀各不相同,因此标准的C++头文件没有指定后缀
4C++中的文件输入输出
头文件:#include<fstream>
使用文件输入输出实例:
#include<fstream> //为了打开一个输出文件,先声明一个ofstream类型的对象: ofstreamoutfile("name-of-file"); //为了测试是否已经成功打开了一个文件,如下判断: //如文件不能打开值为false if(!outfile) cerr<<"Sorry!Wewereunabletoopenthefile!\n"; //为了打开一个输入文件,先声明一个ifstream类型的对象: ifstreaminfile("nameoffile"); if(!infile) cerr<<"Sorry!Wewereunabletoopenthefile!\n"; 一个简单程序: #include<iostream> #include<fstream> #include<string> intmain() { ofstreamoutfile("out_file"); ifstreaminfile("in_file"); if(!infile){ cerr<<"error:unabletoopeninputfile!\n"; return-1; } if(!outfile){ cerr<<"error:unabletoopenoutputfile!\n"; return-2; } stringword; while(infile>>word) outfile<<word<<''; return0; }
头文件里有些什么?
头文件的使用主要体现在两个方面,一个是重(音chóng)用(即多次使用),另一个是共用。
那些提供标准库函数的头文件就是为了重用。很多程序或工程可能会用到这些标准库函数,把它们写在头文件里面,每次使用的时候只需要包含已经完成的头文件就可以了。
头文件的共用主要体现在C++的多文件结构中。由于目前的程序规模较小,尚不需要用到多文件结构,所以在此对头文件的共用不作展开。有兴趣的读者可以查阅相关书籍。
那么,如果我们要自己编写一个可以重用的头文件,里面应该写些什么呢?
类似于标准库函数,我们在头文件里面应该模块化地给出一些函数或功能。另外还应该包括独立实现这些函数或功能的常量、变量和类型的声明。
下面我们就来看一个头文件应用的实例:
//shape.h #include"math.h"//在计算三角形面积时要用到正弦函数 constdoublepi=3.14159265358;//常量定义 structcircle//类型声明 { doubler; }; structsquare { doublea; }; structrectangle { doublea,b; }; structtriangle { doublea,b,c,alpha,beta,gamma; }; doubleperimeter_of_circle(doubler)//函数定义 { return2*pi*r; } doublearea_of_circle(doubler) { returnpi*r*r; } doubleperimeter_of_square(doublea) { return4*a; } doublearea_of_square(doublea) { returna*a; } doubleperimeter_of_rectangle(doublea,doubleb) { return2*(a+b); } doublearea_of_rectangle(doublea,doubleb) { returna*b; } doubleperimeter_of_triangle(doublea,doubleb,doublec) { returna+b+c; } doublearea_of_triangle(doublea,doubleb,doublegamma) { returnsin(gamma/180*pi)*a*b/2; } //main.cpp #include"iostream.h" #include"shape.h"//包含我们编写好的shape.h intmain() { circlec={2}; squares={1}; rectangler={2,3}; trianglet={3,4,5,36.86989,53.13011,90}; cout<<"Perimeterofcircle"<<perimeter_of_circle(c.r)<<endl; cout<<"Areaofsquare"<<area_of_square(s.a)<<endl; cout<<"Perimeterofrectangle"<<perimeter_of_rectangle(r.a,r.b)<<endl; cout<<"Areaoftriangle"<<area_of_triangle(t.b,t.c,t.alpha)<<endl; return0; }
运行结果:
Perimeterofcircle12.5664 Areaofsquare1 Perimeterofrectangle10 Areaoftriangle6
我们编写好了shape.h头文件,以后用到计算图形周长或面积的时候,就不需要重新编写函数了,只需要包含这个头文件就行了。