关于c++:C防止头文件被重复引入的3种方法

6次阅读

共计 2509 个字符,预计需要花费 7 分钟才能阅读完成。

在之前咱们具体介绍了 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 在发挥作用。

正文完
 0