2

C++:头文件篇

 3 years ago
source link: https://dawnki.github.io/2017/08/25/cpp%E5%A4%B4%E6%96%87%E4%BB%B6/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

C++:头文件篇

2017-08-25

在学习c++,被头文件搞得一头包,时常出现重定义的错误,今天就理一下c++的头文件,彻底把它搞懂。C++与C一样,所有变量都需要先声明才能使用,这种”啥都别说,先报名字”的做法,不仅应用在了变量身上,一个函数也需要先声明后使用,并且这个函数只能定义一次。

void A()
B(); // 未声明调用
void B()
// your code

上面的例子中,由于B出现的顺序在A之后,但是A先调用未声明B,因此会报错。解决办法如下:

void B(); //此处声明
void A()
void B() //此处定义(实现)
// your code

上面的知识很简单,学过c基础的同学都知道,上面的例子是说明,声明与定义不一样,调用函数前,这个函数一定是要 先声明 (可以声明无数次) ,其函数的定义可以延后,并且 只能定义一次 。明白了要点之后回到正题。

C++和其他语言一样可以分别编译,用于将代码分成若干个cpp,每个cpp负责不同的功能。如果但是几个cpp文件相互依赖的话,我们直接include cpp源文件 容易出现重复定义的错误。至于为什么会重复定义呢?这是因为include的时候,相当于把目标文件的内容包含进来,比如A包含了B,因为B里面的某个函数有具体的定义(实现),所以A里面就相当定义了一次B里面的函数,但是等到编译B的时候,B也定义了一次函数,导致重复定义的错误。

不犯重复定义的函数也很简单,不需要include源文件,直接在依赖者中(a.cpp)先声明然后使用即可。

//a.cpp
void test();
int main()
test();
//b.cpp
void test()
// your code

现在弄出个头文件的目的就是,比如A要使用B的函数f(),A肯定不能包含具体定义的文件(重复定义),A就包含具有B文件里所有函数声明的头文件B.h,要知道多次定义会报错但是多次声明不会报错。因此,C++推荐的写法是定义与声明分开,头文件写声明,源文件写定义,源文件之间依赖通过包含源文件的头文件来实现。同时,头文件时一个存着一堆声明文件,因此就不用像上面的代码那样,在依赖者中写入被依赖者的声明(解耦)。

现在存在的一个场景是,源文件A依赖源文件B的f(),源文件A叫做A.cpp,源文件B叫做B.cpp,源文件B的头文件叫做B.h

/* A.cpp */
# include "B.h"
int main()
/* B.h */
#ifndef project_B_H //头文件保护措施
#define project_B_H
void f(); //声明
#endif
/* B.cpp */
# include "B.h"
void f() // 实现
// your code

在上述例子中,B的头文件里面由一些宏定义,这些是为了防止重复定义,具体的场景是,两个源文件include了同一个头文件。而编译时,这两个源文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突,因此需要加上#ifndef和#endif,当中ifndef和define后面跟是一个唯一标识,通常规范的格式为项目名_类名_H(H代表头)

如果换成类的写法就是这样

/* A.cpp */
# include "B.h"
int main()
/* B.h */
#ifndef project_B_H //头文件保护措施
#define project_B_H
class B {
public:
void f();
#endif
/* B.cpp */
# include "B.h"
B::f()
// your code

当然具体的实现也可直接写在头文件里,完全没有问题。

include “” 与 include <>

#include < file > 编译程序会先到 标准函数库 中找文件

#include “file” 编译程序会先从 当前目录 中找文件

头文件中可能会出现的extern关键字

在C语言中,修饰符extern用在变量或者函数的声明前,用来说明此变量/函数是在别处定义的,要在此处引用。

在C++中extern还有另外一种作用,用于指示C或者C++函数的调用规范。比如在C++中调用C库函数,就需要在C++程序中用extern “C”声明要引用的函数。这是给链接器用的,告诉链接器在链接的时候用C函数规范来链接。主要原因是C++和C程序编译完成后在目标代码中命名规则不同,用此来解决名字匹配的问题。

老实说,这种定义与声明分开的写法还真有点蛋疼,如果一改就要改两处,所以感觉如果定义与声明都写在头文件里,然后一个include就完事了,这种做法比较方便点。当中应该考虑到兼容c等各种历史原因吧~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK