C++ 自学教程 LearnCPP 第1.9章 头文件


前言

之前的几周兵荒马乱,断更了一段时间,希望大家多包涵。 之后还是希望能坚持翻译下去,另外最近开始用TensorFlow,有些小心得也会整理成笔记写出来,希望能帮助到一两个程序猿/媛吧>0<


头文件,跟他们的目的

当越来越多的文件被加入,程序也变得越来越复杂,一一为不同文件编写所有相关的前置声明就变得非常繁琐。如果能把所有前置声明都放在一个地方,那不是会很方便吗?

事实上,一个完整的C++程序不仅含有C++代码文件(后缀为.cpp),还有头文件(header file),也被称为include file。头文件通常以.h为后缀,不过有些头文件以.hpp为后缀,甚至无后缀。头文件的作用就是存放前置声明,以便其他文件使用。

使用标准库头文件

我们来看看下面这段代码:

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

这段代码用cout输出了“Hello World!”的字符串。但是,这段代码里并没有定义cout。编译器是从哪里知道cout函数的呢?答案就是第一行里“iostream”的头文件。当我们运行#include <iostream>时,我们将头文件<iostream>里所有的内容全部复制到现有文件中。这就使得头文件中所包含的所有内容都能被当前文件使用。

注意,头文件中一般只包含声明。它们不定义具体如何执行某个函数。所以,如果cout只是在头文件中被声明,它的具体定义又在哪里呢?答案是C++运行时库,这个库会在程序连接阶段自动连接。
头文件运行图

想象一下如果iostream头文件不存在的话会怎样。不论何时你使用std::cout, 你需要为所有使用这个函数的文件手动复制所有与它相关的前置声明!这还需要你知道标准库里哪些代码与其相关而哪些无关。而使用#include <iostream>一行就能解决所有问题啦!


编写你自己的头文件

现在我们回顾一下上一节使用的例子,最后我们编写了两个文件:add.cpp和main.cpp,它们分别是:
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;
}

为了在编译主函数的时候让编译器知道“add”函数是什么,我们使用了一个前置声明。如之前所说,为所有函数编写这样的前置声明会变得非常麻烦。而头文件可以为我们解决这个烦恼。头文件只需要被编写一遍,并可以包含所有需要的文件。这也减少了因为改动函数原型而需要改变其他代码的次数,使得代码的维护变得简单。

编写自定义的头文件其实非常简单,它包含两个部分。
第一部分叫做头文件防护(header guard),下一节会具体探讨。头文件防护防止头文件在同一文件中被多次#include包含。

第二部分就是头文件的具体内容了,也就是所有函数的前置声明。头文件的后缀应是.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

要在主函数内使用它,我们需要执行#include操作
主函数:

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

而add.cpp不变

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

当编译器编译#include 这行代码时,它将add.h里的所有内容都复制到主函数的文件里。因为add.h里有对add()的函数原型,这个函数原型就可以作为add()的前置声明了!

最终,我们的程序会编译并顺利运行。

注:当你#include某个文件,这个文件的全部内容都会被复制并插入这个位置。
头文件包含过程

如果你的编译器报错说找不到add.h,确保这个头文件名字正确,其后缀不是.hpp或者.txt。

尖括号与引号

你可能会注意到我们包含iostream使用了尖括号,而对add.h使用了引号。这样做的原因是,尖括号告诉编译器我们想要包含一个编译器内置的头文件,编译器会在系统目录里查找这个头文件。而双引号则意味着我们想包含一个自定义头文件,所以编译器会在当前目录下寻找这个头文件。当编译器没有找到该文件时,才会在不同路径下搜索。

规则:系统自带头文件使用尖括号,而自定义头文件使用双引号。

头文件中可嵌套头文件,但你不应依赖于这样的头文件。永远确保你的所有.cpp文件中都包含了所有相关的头文件。

规则:每一个.cpp文件中都应明确引用编译所需的所有头文件。

为什么iostream后没有.h后缀?

这是另一个非常常见的问题。这是因为iostream.h与iostream其实是两个不同的头文件。具体的原因涉及到C++的创建历史,大家有兴趣可以留言。

但总之你应该使用iostream,而不是iostream.h。因为后者已经不再被更新/维护了。

如何包含其他目录中的头文件

这也是一个较为常见的问题。一个(不好的)方法就是把文件路径添加到文件名之前,比如说:

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

这种做法的劣势在于你必须丝毫不差的写出代码存储的结构路径,一旦这些路径稍加变动,代码就会报错。
更好的做法是告诉你的编译器你想要使用另一个目录下的文件,它就会自己去那个目录下查找需要的文件。通常你只需要在你的IDE里添加路径就可以了。在visual studio里,右键点击你的项目在属性里查找VC++目录,其中有一行包含路径,在里面添加别的目录就尅了。

我可以在头文件里添加变量定义吗?

不可以,这样可能会使得连接器报错,后面课程会具体讲解这个情况。


头文件编写/使用指南

下面是编写和使用头文件时应该遵守的规则:
• 永远都包含头文件防护
• 不要在头文件里定义除了常量以外的变量。头文件之应用于声明。
• 不要在头文件定义函数。
• 每一个头文件都应有其具体作用,互相之间越独立越好。
• 头文件名应该与其相关的源代码文件名一致(例如add.h与add.cpp)
• 试着包含尽可能少的头文件。
• 不要将“.cpp”文件作为头文件


说明: 这系列笔记是基于网上一个英文教程LearnCPP

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值