Bootstrap

C++杂谈——关于C++多文件编译时会想到的问题(头文件,函数作用域)

笔者以前只写过用来做题的代码(OIer),所以当接触到多文件时里函数被定义后居然能在其他文件里使用,真的是一头雾水。所以这篇文章的讨论的内容是在多文件编译的过程中遇到的一些问题及解答。

头文件

开始编写多文件,想必最先遇到以前没用过的东西就是头文件了吧。

明明我们明明能在cpp里定义函数,类,为什么非要自找麻烦,专门写一个.h的头文件呢?

这个问题大部分是便于开发者的一种设计,是为了将声明与实现分开。当我们需要修改实现时,只需要修改实现的源文件(a.cpp),而不需要修改用户使用的接口(a.h)。这能大大简化开发流程。
我们目前编写的程序内容少或许没什么体会。但举个例子,一个游戏里的角色有武器,防具,饰品等等,当我们需要修改武器的内容时,用头文件的你只需要修改weapon.cpp里的内容再重新编译即可。但没使用头文件的小明却必须修改main.cpp里每一个使用武器的地方。其中复杂度差异可想而知。


作用域

讲完了头文件的作用,不知道你会不会想到这个问题:

我在a.h中声明了函数 f f f,并在a.cpp中给出了函数 f f f的实现。a.cpp中使用了#include “a.h”,所以这两份代码中的函数联系起来了,很符合直觉。但是b.cpp想使用函数 f f f,同样也只#include “a.h”,但是b.cpp却能知道a.cpp里函数是如何实现的,这似乎就不符合直觉了。

于是,我们便来到了关于C++里函数,变量,类等作用域的的问题。

先说结论,在函数外定义的变量,函数(函数本来就不能定义在函数里),在通过CMake编译到一起后,不同文件会共享。具体实现过程,用粗浅的理解 ,可以认为声明告诉C++里有什么函数,做成了类似函数字典的东西,后来看到了函数的实现,便把实现再加到字典中。以后调用有声明和实现的函数就像查字典的过程一样。

因此,源文件(a.cpp)中实现的函数相当于是为整个文件提供了函数的实现。

当然,这样也会衍生出可能会多次重复定义同一个函数的问题,这也是为什么我们会在头文件中写成如下格式:

#ifndef USER_H
#define USER_H

/*code*/

#endif

这三行做的事就是判断是否使用过这个头文件(定义过函数),以防重复定义报错。


这篇博文讲到这里就结束了,以后遇到新的有意思的问题还会再更新这个系列。

;