C++头文件和源文件深入解析

前言

C++中头文件的重复包含问题是新手几乎一定会遇到的问题,这涉及到C++中一个关键的概念,C++允许多次声明,但不允许多次定义。多次包含头文件会导致重复定义,从而使文件链接报错,有时候这种错误很难排查,解决起来也十分麻烦。本文力求以一个通俗易懂的方式且从原理层面解释,如何解决头文件的重复包含问题。

前置知识:

  • 对编译过程有一定的了解
  • 明白声明和定义的区别

头文件的由来

C++采用分离编译方式,即代码段分布在不同的源文件中,有些函数或功能需要在不同文件中使用,于是需要在编译期间通过编译器将代码链接起来。
如A中定义了a函数,B中定义了b函数,A要使用B中定义的函数,则首先要在A中声明b函数,然后编译器在链接期间寻找该函数的定义。但如果文件一旦多起来,每使用一个函数都要声明,一但函数需要修改,工作会十分复杂,导致难以维护和使用。
于是C++引入头文件

头文件的作用

头文件通常只包含那些声明内容(C++允许多次声明,但不允许多次定义),在程序编译过程中,通过#include,将.h文件原原本本复制到.cpp文件中,这样子就解决了声明繁琐的问题。需要用到某文件中的某函数或功能,只需要#include某文件,这样在使用时就不用再声明了。

头文件的注意事项

于是可以很容易明白,为什么头文件中不应该包含定义内容,非常容易产生重复定义的问题。因为一个头文件的内容通常会被复制到多个源文件中,如果此时头文件中有定义内容,就会产生重复定义,导致编译器报错。
但有三个例外:

  • 头文件可以写const或static对象的定义,如果多个源文件链接到同一个const或static对象,实际上各个文件作用域内都会生成同名的独立的变量,多个文件并不是共享同一个对象。
  • 头文件中可以写内联函数的定义,内联函数实际上是一种展开而不是普通函数的链接。编译器会在使用内联函数的地方将内联函数展开,即用内联函数体替换掉函数的调用。而使用普通函数会调用函数指针,是一种链接的关系。因此内联函数可以多次定义,但需要保证多次定义的方式和内容是一致的
  • 头文件中可以写类的定义,也可以包含类中成员函数的具体实现,此时该函数被视为内联的。但要注意成员函数的定义必须在类内部,在类外部的定义是非法的。

防止头文件重复包含的方法

方法一:#ifndef#define

此方法通过宏定义来解决重复包含问题,#define定义一个宏,#ifndef判断该宏是否已经定义,如果没有定义则执行#ifndef#endif中的代码内容,否则什么都不做。所以一旦包含了头文件,那么宏就定义了,如果再次进行包含,#ifndef条件判断会跳过包含头文件的代码内容。

下面以一个例子帮助理解:

#ifndef __FILE_H__
#define __FILE_H__
//声明、定义语句
#endif

//例子
//a.h
#ifndef A_H
#define A_H
#include "c.h"
#endif

//c.h
#ifndef C_H
#define C_H
...
#endif

//b.cpp
#include "a.h"//a.h中包含c.h,所以C_H被定义
#include "c.h"//由于C_H被定义,所以不会重复定义

如果没有进行宏定义,由于a.h包含c.h,所以b.cpp引入a.h的同时也会引入c.h,此时又引入c.h就会导致重复包含头文件。

方法二:#pragma once

此方法可以保证同一个文件不会被多次包含,但如果一份文件有多份拷贝(内容相同,但文件名不同),此方法不能保证它们不被重复包含。但方法一的宏可以,因为内容相同,同名的宏只会定义一次,即使文件名不同,多次包含也不会导致重复包含。

//只需要在.h文件添加这一行代码即可
#pragma once
上一篇 下一篇

评论 | 0条评论