c++ 防止头文件重复引入的三种方法
在之前我们详细介绍了C语言中如何使用宏定义(#ifndef/#define/#endif)来有效避免头文件被重复#include,此方式在C++多文件编程中也很常用。
举个例子,如下是一个C++项目,其内部含有school.h和student.h这2个头文件以及main.cpp源文件,其各自包含的代码为:
//student.h classStudent{ //...... }; //school.h #include"student.h" classSchool{ //...... private: Studentstu[50]; }; //main.cpp #include"student.h" #include"school.h" intmain(){ //...... return0; }
运行此项目会发现,编译器报“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 classStudent{ //...... }; #endif
虽然该项目main.cpp文件中仍#include了2次"student.h",但鉴于_STUDENT_H宏只能定义一次,所以Student类也仅会定义一次。再次执行该项目会发现,其可以正常执行。
2)使用#pragmaonce避免重复引入
除了前面第一种最常用的方式之外,还可以使用#pragmaone指令,将其附加到指定文件的最开头位置,则该文件就只会被#include一次。
我们知道,#ifndef是通过定义独一无二的宏来避免重复引入的,这意味着每次引入头文件都要进行识别,所以效率不高。但考虑到C和C++都支持宏定义,所以项目中使用#ifndef规避可能出现的“头文件重复引入”问题,不会影响项目的可移植性。
和ifndef相比,#pragmaonce不涉及宏定义,当编译器遇到它时就会立刻知道当前文件只引入一次,所以效率很高。
但值得一提的是,并不是每个版本的编译器都能识别#pragmaonce指令,一些较老版本的编译器就不支持该指令(执行时会发出警告,但编译会继续进行),即#pragmaonce指令的兼容性不是很好。
目前,几乎所有常见的编译器都支持#pragmaonce指令,甚至于VisualStudio2017新建头文件时就会自带该指令。可以这么说,在C/C++中,#pragmaonce是一个非标准但却逐渐被很多编译器支持的指令。
除此之外,#pragmaonce只能作用于某个具体的文件,而无法向#ifndef那样仅作用于指定的一段代码。
这里仍以前面的"student.h"文件为例,将其内容修改为:
#pragmaonce classStudent{ //...... };
3)使用_Pragma操作符
C99标准中新增加了一个和#pragma指令类似的_Pragma操作符,其可以看做是#pragma的增强版,不仅可以实现#pragma所有的功能,更重要的是,_Pragma还能和宏搭配使用。
有关_Pragma操作符更多的功能和用法,本节不做详细讲解,这里仅介绍如何用_Pragma操作符避免头文件重复引入。
当处理头文件重复引入问题时,可以将如下语句添加到相应文件的开头:
_Pragma("once")
比如,将该语句添加到前面项目中student.h文件中的开头位置,再次执行项目,其可以正常执行。
事实上,无论是C语言还是C++,为防止用户重复引入系统库文件,几乎所有库文件中都采用了以上3种结构中的一种,这也是为什么重复引入系统库文件编译器也不会报错的原因。
总结
本节介绍了3种避免头文件被重复引入的方法,其中#pragmaonce和_Pragma("once")可算作一类,其特点是编译效率高,但可移植性差(编译器不支持,会发出警告,但不会中断程序的执行);而#ifndef的特点是可移植性高,编译效率差。读者可根据实际情况,挑选最符合实际需要的解决方案。
除非对项目的编译效率有严格的要求,强烈推荐读者选用第一种解决方案,即采用#ifndef/#define/#endif组合解决头文件被重复引入。
另外在某些场景中,考虑到编译效率和可移植性,#pragmaonce和#ifndef经常被结合使用来避免头文件被重复引入。比如说:
#pragmaonce #ifndef_STUDENT_H #define_STUDENT_H classStudent{ //...... }; #endif
当编译器可以识别#pragmaonce时,则整个文件仅被编译一次;反之,即便编译器不识别#pragmaonce指令,此时仍有#ifndef在发挥作用。
以上就是c++防止头文件重复引入的三种方法的详细内容,更多关于c++头文件重复引入的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。