Learning C++ 之1.9a 头文件

头文件以及目的:

随着程序变得越来越大,在不同的文件引入其他文件中的函数变得异常复杂。那么为什么不把这些声明提前放到一个统一的地方呢?

C++中不只有.cpp的代码文件,另一种类型文件叫做头文件。头文件的后缀名一般是.h,有时候是.hpp。头文件的目的是包含其他文件使用的声明。

使用标准的库函数头文件:

看下面的程序

#include <iostream>
int main()
{
    std::cout << "Hello, world!" << std::endl;
    return 0;
}

这个程序输出Hello World到屏幕上。然而你可能会问,cout这个函数并没有定义过,为什么可以使用呢?答案就是#incude <iostream>  ,cout函数在iostream中,这里我们引用了该文件就表示把iostream中的函数都copy过来了,所以cout就可以使用了。头文件中的声明这样就可以通过include,在我们的代码中使用了。

切记,头文件只包含函数的声明,并不包含函数的实现。那么cout只是声明了,在哪里具体实现的呢。他是在C++运行支持库中实现的,当link的时候会自动链接到你的程序上。


设想如果没有头文件的话,我们需要使用std::cout就需要把相关的声明手动的copy到你所使用的文件中,而且你需要知道那些是相关的,哪些不是。相比较而言,直接include进来更加容易。

写你自己的头文件:

现在让我们回到之前的例子中,我们有两个文件如下:

add.cpp

int add(int x, int y)
{
    return x + y;
}

main.cpp

#include <iostream>
 
int add(int x, int y); // forward declaration using function prototype
 
int main()
{
    std::cout << "The sum of 3 and 4 is " << add(3, 4) << std::endl;
    return 0;
}
我们用了提前声明的方法来保证main.cpp可以使用add.cpp中的函数,为你的代码中写所有的函数的声明很快会让你感到乏味的。

头文件帮忙我们解决了这个负担问题,函数在头文件中只需要声明一次,然后就可以通过include的方式随处引用。这同样减少了很多工作量,当你的一个函数需要改动的时候,不会去更改每一处引用的函数的地方,只需要改头文件就行了。

写我们自己的头文件非常简单,一般头文件包含两部分:

第一部分称作heaher Guard,这一部分可以有效地防止一个程序多次引用同一个头文件。

第二部分是真正的内容部分,这里面包含了所有需要被其他代码调用的函数声明,因为头文件以.h为文件尾,所以我们可以成为add.h。

add.h

// This is start of the header guard.  ADD_H can be any unique name.  By convention, we use the name of the header file.
#ifndef ADD_H
#define ADD_H
 
// This is the content of the .h file, which is where the declarations go
int add(int x, int y); // function prototype for add.h -- don't forget the semicolon!
 
// This is the end of the header guard
#endif

main.cpp

#include <iostream>
#include "add.h"
 
int main()
{
    std::cout << "The sum of 3 and 4 is " << add(3, 4) << std::endl;
    return 0;
}

当编译器编译到#include “add.h”的时候,编译器会把add.h中的内容copy到相应的文件里。因为add.h中年有add()函数。所以这个地方就作为了add函数的一个提前声明。

整个流程如下图所示:


<>和“”的区别:

你可能会有疑问,为什么同样是include文件,一个是<>而另外一个是""呢。是这样的,<>是C++标准的库,而""是你自己的一些头文件。程序里找<>文件的时候会直接去C++系统中的头文件库查找,而.h文件会从当前目录直接查找,如果找不到,再去其他目录查找,直到检查系统的目录。

头文件可能会重复引用,这些你不用担心,只需要确认你所需要的文件都被include进来了就行了。

为什么iostream没有一个.h扩展呢?

这个问题也常常被提及,答案是iostream和iostream.h是两个不同的头文件,这个有个历史渊源。

最初创建C++的时候,所有的C++的头文件都是以.h结尾的。直到C++加入了ANSI标准委员会,他们想把运行库的头文件全部加载到std的命名空间里面。然而,如果这样的话,纳闷之前的C++版本写的code都不能正常运行了。

为了避免重写所有的代码,一系列内容相同,但是没有后缀的头文件创建了,放到了std命名空间里面。这些新的头文件都没有后缀。这样一来确保了之前的老的程序还可以正常工作。

当你从标准库里引用文件的时候,确保使用的是std里面的没有后缀的文件,而不是带有.h的文件,这样可能会引起版本不支持的问题。

另外一些C的标准库函数都统一增加了c前缀,如stdlib.h替换成cstdlib。这些头文件同样移到了std命名空间里面。

然而当你自己写头文件的时候,还是要求带有.h文件的。因为你的头文件不在std命名空间里。

原则:只要库里有非.h文件的头文件,那么就用没有.H的文件。不然就用.h的头文件。

包含来自其他目录的头文件

另一个普遍的问题就是怎么包含来自其他目录的头文件。

一个比较糟糕的做法是使用相对路径来包含,如下面的例子:

#include "headers/myHeader.h"
#include "../moreHeaders/myOtherHeader.h"

这种做法的缺点是你的路径必须相对固定,如果更新一下排版,可能就会有问题了。

最好的版本是告诉你的编译器你有专门放置头文件的一个地方,这样在当前目录找不到的时候,就回去你指定的地方找。这个在你的IDE里可以实现。

VS:Soulution Project 右键,Properties,VC++ Directory tab,Include Directories

Code::Block:  BUild Options,Search Directories

g++:使用-I参数

g++ -o main -I /source/includes main.cpp

这种方式的好处是,不管你怎么改变代码路径,你只需要在编译文件或者IDE里改一下相关路径就OK了。

是否可以往头文件里放置函数?

不行,会导致链接错误。在之后的课程里面会讲到原因-header guards。只是声明就可以了。

头文件最好建议:

下面是几点好的写头文件的建议:

  • 必须包含head guards
  • 不能在头文件里定义变量,除非他们是常数。头文件只能用来声明。
  • 不要在头文件里定义函数
  • 每一个头文件都有独立性,做一类特殊的服务。比如你需要把A函数相关的函数放到A.h里面,把B函数相关的函数放到B.h里面。这样不会造成混淆。
  • 将你的头文件和源文件命名相同
  • 尽可能少地include文件,和代码无关的头文件不要include
  • 不要include C++文件
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章