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

 更新时间:2021年02月25日 16:40:18   作者:C语言编程学习基地  
这篇文章主要介绍了c++ 防止头文件重复引入的三种方法,帮助大家更好的理解和学习使用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++ 头文件重复引入的资料请关注脚本之家其它相关文章!

相关文章

  • VC++开发中完美解决头文件相互包含问题的方法解析

    VC++开发中完美解决头文件相互包含问题的方法解析

    本文中,为了叙述方便,把class AClass;语句成为类AClass的声明,把class AClass开始的对AClass的类成员变量、成员函数原型等的说明称为类的定义,而把在CPP中的部分称为类的定义
    2013-09-09
  • 解析C++ 浮点数的格式化输出

    解析C++ 浮点数的格式化输出

    本篇文章是对C++中浮点数的格式化输出进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • Linux下g++编译与使用静态库和动态库的方法

    Linux下g++编译与使用静态库和动态库的方法

    下面小编就为大家带来一篇Linux下g++编译与使用静态库和动态库的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • C语言 数据类型详细介绍

    C语言 数据类型详细介绍

    本文主要讲解C语言 数据类型,这里整理了详细的数据类型的资料,希望能帮助刚刚开始学习C语言的同学
    2016-08-08
  • C语言进阶练习二叉树的递归遍历

    C语言进阶练习二叉树的递归遍历

    树是一种重要的非线性数据结构,直观地看,它是数据元素(在树中称为结点)按分支关系组织起来的结构,很象自然界中的树那样。树结构在客观世界中广泛存在,如人类社会的族谱和各种社会组织机构都可用树形象表示,本篇介绍二叉树的递归与非递归遍历的方法
    2022-06-06
  • C语言实现在控制台打印余弦曲线

    C语言实现在控制台打印余弦曲线

    余弦曲线又叫余弦波(cosinwave),是一种来自数学三角函数中的余弦比例的曲线。这篇文章主要为大家介绍了如何在控制台绘制余弦曲线,感兴趣的可以了解一下
    2023-02-02
  • QT连接Mysql数据库的实现步骤

    QT连接Mysql数据库的实现步骤

    本文主要介绍了QT连接Mysql数据库的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • C++读写Excel的实现方法详解

    C++读写Excel的实现方法详解

    本篇文章是对C++读写Excel的实现方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C语言中结构体和共用体实例教程

    C语言中结构体和共用体实例教程

    这篇文章主要给大家介绍了关于C语言中结构体和共用体的相关资料,结构体是一种自定义的复合数据类型,共用体也叫联合体,使几个不同类型的变量共占一段内存(相互覆盖),需要的朋友可以参考下
    2021-06-06
  • C语言动态内存管理malloc柔性数组示例详解

    C语言动态内存管理malloc柔性数组示例详解

    这篇文章主要为大家介绍了C语言动态内存管理malloc柔性数组示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10

最新评论