c++ 防止头文件重复引入的三种方法

在之前我们详细介绍了 C 语言中如何使用宏定义(#ifndef / #define / #endif)来有效避免头文件被重复 #include,此方式在 C++ 多文件编程中也很常用。

举个例子,如下是一个 C++ 项目,其内部含有 school.h 和 student.h 这 2 个头文件以及 main.cpp 源文件,其各自包含的代码为:

//student.h

class Student {

    //......

};

//school.h

#include "student.h"

class School {

    //......

private:

    Student stu[50];

};

//main.cpp

#include "student.h"

#include "school.h"

int main() {

    //......

    return 0;

}

运行此项目会发现,编译器报“Student 类型重定义”错误。这是因为在 school.h 文件中已经 #include 了一次 "student.h",而在 main.cpp 主程序又同时 #include 了 "school.h" 和 "student.h",即 Student 类的定义被引入了 2 次,C++不允许同一个类被重复定义。

有小伙伴可能想到,既然 School.h 文件中已经引入了 Student 类,那去掉 main.cpp 主程序引入的 student.h 文件不就可以了吗?这样确实可以避免重复引入 Student 类,但此方式并不适用于所有“重复引入”的场景。

C++ 多文件编程中,处理“多次 #include 导致重复引入”问题的方式有以下 3 种。

————————

1) 使用宏定义避免重复引入

在实际多文件开发中,我们往往使用如下的宏定义来避免发生重复引入:

#ifndef _NAME_H

#define _NAME_H

//头文件内容

#endif

其中,_NAME_H 是宏的名称。需要注意的是,这里设置的宏名必须是独一无二的,不要和项目中其他宏的名称相同。

当程序中第一次 #include 该文件时,由于 _NAME_H 尚未定义,所以会定义 _NAME_H 并执行“头文件内容”部分的代码;当发生多次 #include 时,因为前面已经定义了 _NAME_H,所以不会再重复执行“头文件内容”部分的代码。

也就是说,我们可以将前面项目中的 student.h 文件做如下修改:

#ifndef _STUDENT_H

#define _STUDENT_H

class Student {

    //......

};

#endif

虽然该项目 main.cpp 文件中仍 #include 了 2 次 "student.h",但鉴于 _STUDENT_H 宏只能定义一次,所以 Student 类也仅会定义一次。再次执行该项目会发现,其可以正常执行。

2) 使用#pragma once避免重复引入

除了前面第一种最常用的方式之外,还可以使用 #pragma one 指令,将其附加到指定文件的最开头位置,则该文件就只会被 #include 一次。

我们知道,#ifndef 是通过定义独一无二的宏来避免重复引入的,这意味着每次引入头文件都要进行识别,所以效率不高。但考虑到 C 和 C++ 都支持宏定义,所以项目中使用 #ifndef 规避可能出现的“头文件重复引入”问题,不会影响项目的可移植性。

和 ifndef 相比,#pragma once 不涉及宏定义,当编译器遇到它时就会立刻知道当前文件只引入一次,所以效率很高。

但值得一提的是,并不是每个版本的编译器都能识别 #pragma once 指令,一些较老版本的编译器就不支持该指令(执行时会发出警告,但编译会继续进行),即 #pragma once 指令的兼容性不是很好。

目前,几乎所有常见的编译器都支持 #pragma once 指令,甚至于 Visual Studio 2017 新建头文件时就会自带该指令。可以这么说,在 C/C++ 中,#pragma once 是一个非标准但却逐渐被很多编译器支持的指令。

除此之外,#pragma once 只能作用于某个具体的文件,而无法向 #ifndef 那样仅作用于指定的一段代码。

这里仍以前面的 "student.h" 文件为例,将其内容修改为:

#pragma once

class Student {

    //......

};

3) 使用_Pragma操作符

C99 标准中新增加了一个和 #pragma 指令类似的 _Pragma 操作符,其可以看做是 #pragma 的增强版,不仅可以实现 #pragma 所有的功能,更重要的是,_Pragma 还能和宏搭配使用。

有关 _Pragma 操作符更多的功能和用法,本节不做详细讲解,这里仅介绍如何用 _Pragma 操作符避免头文件重复引入。

当处理头文件重复引入问题时,可以将如下语句添加到相应文件的开头:

_Pragma("once")

比如,将该语句添加到前面项目中 student.h 文件中的开头位置,再次执行项目,其可以正常执行。

事实上,无论是 C 语言还是 C++,为防止用户重复引入系统库文件,几乎所有库文件中都采用了以上 3 种结构中的一种,这也是为什么重复引入系统库文件编译器也不会报错的原因。

总结

本节介绍了 3 种避免头文件被重复引入的方法,其中 #pragma once 和 _Pragma("once") 可算作一类,其特点是编译效率高,但可移植性差(编译器不支持,会发出警告,但不会中断程序的执行);而 #ifndef 的特点是可移植性高,编译效率差。读者可根据实际情况,挑选最符合实际需要的解决方案。

除非对项目的编译效率有严格的要求,强烈推荐读者选用第一种解决方案,即采用 #ifndef / #define / #endif 组合解决头文件被重复引入。

另外在某些场景中,考虑到编译效率和可移植性,#pragma once 和 #ifndef 经常被结合使用来避免头文件被重复引入。比如说:

#pragma once

#ifndef _STUDENT_H

#define _STUDENT_H

class Student {

    //......

};

#endif

当编译器可以识别 #pragma once 时,则整个文件仅被编译一次;反之,即便编译器不识别 #pragma once 指令,此时仍有 #ifndef 在发挥作用。

以上就是c++ 防止头文件重复引入的三种方法的详细内容,更多关于c++ 头文件重复引入的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++中#include头文件的示例详解

    fstream是C++ STL中对文件操作的合集,包含了常用的所有文件操作.在C++中,所有的文件操作,都是以流(stream)的方式进行的,fstream也就是文件流file stream. 最常用的两种操作为: 1.插入器(<<) 向流输出数据.比如说打开了一个文件流fout,那么调用fout<<"Write to file"<<endl;就表示把字符串"Write to file"写入文件并换行. 2.析取器(>>

  • 简单谈谈C++ 头文件系列之(algorithm)

    简介 algorithm头文件是C++的标准算法库,它主要应用在容器上. 因为所有的算法都是通过迭代器进行操作的,所以算法的运算实际上是和具体的数据结构相分离的 ,也就是说,具有低耦合性. 因此,任何数据结构都能使用这套算法库,只要它具有相应的迭代器类型. 算法类别 如上图所示,库中的算法主要分为4类: 非修改性顺序操作(Non-modifying sequence operations) 可变顺序操作(Mutating sequence operations) 排序和关系操作(Sorting

  • 简单谈谈C++ 头文件系列之(bitset)

    简介 该头文件有关位集,实际上是vector 位 位本质上对应bool的概念,只有0或1,true或false两种对立的值. 但很可惜,字节才是机器上最小的存储单元,所以bool基本上是由一个字节大小. bitset是出于高效的空间利用为目的才出现的. 位操作 operator [] : 通过下标访问bit. count : 计数位值为1的位个数. size : 返回位的大小,即有多少个位. test : 测试下标指向的位值是否为1. any : 判断是否有任何一个位值为1. none : 判断

  • 简单谈谈C++ 头文件系列之(iosfwd)

    简介 输入输出历来都是语言的重要部分,在C++中,该库也是占据了相当大的一部分. C++的输入输出库是其遵循面向对象设计的结果,并结合了泛型编程. 以下是这些库类的关系图(箭头标示继承,白框表示摸板,黑框表示摸板实例): iosfwd声明 该文件的全称是"input output stream forward",即输入输出流前向声明. 顾名思义,该头文件的主要目的就是为用户提供所有输入输出类的声明. 当你在某些只需要这些类声明,而不需要定义的地方 (例如:自定义的头文件中),就可以简单

  • C/C++语言中的头文件汇总

    stdio.h就是指"standard input&output" 意思就是说标准输入输出头文件! 所以了,用到标准输入输出函数时,就要调用这个头文件 C/C++头文件 include <assert.h> //设定插入点 include <ctype.h> //字符处理 include <errno.h> //定义错误码 include <float.h> //浮点数处理 include <fstream.h> //

  • C++万能库头文件在vs中的安装步骤(图文)

    前言 C++万能库头文件在vs2019中的安装 一.C++万能库是什么? 1.C++万能库的定义:简而言之,就是一条语句代替了多条语句,包含了目前c++所包含的所有头文件 2.C++万能库的写法:#include <bits/stdc++.h> 二.C++万能库包含的内容: 如下是bits/stdc++.h所包含的C++所有库函数头文件: #ifndef _GLIBCXX_NO_ASSERT #include <cassert> #endif #include <cctype

  • C++ 头文件系列(set)详解

    简介 头文件包含 set . multiset 两个类模版,这里要描述的概念与map非常相似,甚至连成员函数都几乎一样,所以这篇随笔会很短. set set如果翻译成中文应该是集合的意思,这里更确切的说是 唯一有序集合 ,性质与map类似: 关联性 元素唯一性 动态增长 有序性 此外的一个重要特点是: Key与Value是同一个对象(自映射) set == map 定义使用set的时候只需要传入一个类型参数,这个类型即是key,也是value. 实际上, set是map的特殊情况 ,虽然set没

  • C++中头文件和源文件详细介绍

    C++中的头文件和源文件详解 一.C++编译模式 通常,在一个C++程序中,只包含两类文件--.cpp文件和.h文件.其中,.cpp文件被称作C++源文件,里面放的都是C++的源代码:而.h文件则被称作C++头文件,里面放的也是C++的源代码. C+ +语言支持"分别编译"(separate compilation).也就是说,一个程序所有的内容,可以分成不同的部分分别放在不同的.cpp文件里..cpp文件里的东西都是相对独立的,在编 译(compile)时不需要与其他文件互通,只需要

  • VScode编译C++ 头文件显示not found的问题

    一直用codeblocks,想试试vscode,结果这个问题给我弄懵逼了.一开始以为是iostream这个头文件not found,后来发现第一个头文件都会这样显示,放到后面就不会了,然而,光这一个显示not found(虽然并不影响编译),就能逼死强迫症的啊~~~ 言归正传,这个问题解决方法是: 文件--首选项--设置--搜索"clang.diagnostic.enable",然后把勾勾去掉就好了.这是插件C/C++ Clang Command Adapter的一个选项~~ 如果一堆

  • VSCode配置C/C++并添加非工作区头文件的方法

    下文均在Windows环境下: 配置C/C++ 按照教程安装MinGW,并配置gcc和g++,配置方法有两种: 1 GUI配置 在MinGW Installation Manager中选取对应的Package,然后Installation->Apply Changes,如果失败则多试几次. 2 控制台配置 配置系统环境变量: 1.xxx/MinGW/bin; 即安装MinGW目录下的bin文件夹 然后在cmd中输入: 1.mingw-get install gcc g++ mingw32-mak

  • VSCode添加头文件(C/C++)的实现示例

    使用VSCode编译C/C++时,会存在找不到头文件的情况这时候需要设置两个地方: 1.c_cpp_properites.json 2.task.json 以下是我修改的对应的文件 { "configurations": [ { "name": "Win32", "includePath": [ "${workspaceFolder}/**", "${workspaceRoot}", &

  • C++中头文件与源文件的作用详解

    一.C++ 编译模式 通常,在一个 C++ 程序中,只包含两类文件-- .cpp 文件和 .h 文件.其中,.cpp 文件被称作 C++ 源文件,里面放的都是 C++ 的源代码:而 .h 文件则被称作 C++ 头文件,里面放的也是 C++ 的源代码. C++ 语言支持"分别编译"(separatecompilation).也就是说,一个程序所有的内容,可以分成不同的部分分别放在不同的 .cpp 文件里..cpp 文件里的东西都是相对独立的,在编译(compile)时不需要与其他文件互通

随机推荐