在C++中包含两次头文件

pvcm50d1  于 2023-10-20  发布在  其他
关注(0)|答案(6)|浏览(129)

如果我在文件中包含iostream或任何其他头文件两次,会发生什么?我知道编译器不会抛出错误。
代码会被添加两次还是内部会发生什么?
当我们包含头文件时,实际上会发生什么?

aelbi1ox

aelbi1ox1#

Include guard防止编译器实际看到文件的内容两次。
Include guard基本上是一组位于头文件开头和结尾的预处理器条件指令:

#ifndef SOME_STRING_H
#define SOME_STRING_H

//...

#endif

现在,如果你两次包含这个文件,那么第一次宏SOME_STRING_H没有定义,因此文件的内容被编译器处理和看到。但是,由于#ifdef之后的第一个是#define,因此定义了SOME_STRING_H,并且编译器在下一轮不会看到头文件的内容。
为了避免冲突,在包含保护中使用的宏的名称依赖于头文件的名称。

hgb9j2n6

hgb9j2n62#

头文件是简单的野兽。当你#include <header>时,所发生的一切就是header的内容基本上被复制粘贴到文件中。为了防止头文件被多次包含,使用了include guards,这就是为什么在大多数头文件中,您会看到类似于

#ifndef SOME_HEADER_FILE_GUARD 
#define SOME_HEADER_FILE_GUARD

//Contents of Header

#endif
pcrecxhr

pcrecxhr3#

这取决于。除了<assert>之外,标准要求标准头的第二个(以及以后的)包含是无操作的。然而,这是标题的一个特征;编译器将(至少在概念上)在每次遇到include时读取并包括所有的头文本。
在这种情况下避免多个定义的标准做法是使用include guards:头文件中的所有C++代码都将被包含在类似于以下内容中:

#ifndef SPECIAL_NAME
#define SPECIAL_NAME
//  All of the C++ code here
#endif SPECIAL_NAME

显然,每个头需要一个不同的名称。在应用程序中,通常可以根据文件名和位置建立约定;比如subsystem_filename,Map了C符号中不法律的字符(如果你在文件名中使用它们)(而且通常都是大写的)。对于图书馆来说,最好的做法是生成一个合理长度的随机字符序列;更常见的是(尽管从实现质量的观点来看肯定是较差的)确保每个这样的符号开始都有文档化的前缀。
当然,系统库可以使用保留符号(例如,以下划线开头,后跟大写字母的符号),以保证没有冲突。或者它可以使用一些完全不同的、依赖于实现的技术。例如,微软使用编译器扩展#pragma once; g
的用法包括总是以_GLIBCXX开头的保护(在用户代码中这不是一个法律的符号)。这些选项不一定对您可用。

1cklez4t

1cklez4t4#

它只是被跳过,因为预处理器代码沿着以下行沿着:

#ifndef MY_HEADER_H
#define MY_HEADER_H

<actual header code here>

#endif

因此,如果你包含两次,那么MY_HEADER_H已经定义,#ifndef#endif之间的所有内容都被预处理器跳过。

sdnqo3pr

sdnqo3pr5#

当你包含一个头文件时,它的所有内容都会被复制到“#include”指令所在的行。这是由预处理器完成的,是编译过程中的一个步骤。
此过程对于标准文件(如iostream或存储在本地目录中的用户创建的“.h”文件)是相同的。但是,语法略有不同。
对于存储在库中的“iostream”等文件,我们使用#include <filename>。而对于本地目录中的头文件,我们使用#include "filename.h"

现在,如果我们包含两次头文件:

理想情况下,内容应该复制两次。但是...
1.许多头文件使用提到#pragma once的现代实践,这指示预处理器只复制内容一次,无论头文件被包含多少次。
1.一些非常古老的代码使用了一个称为“包含gauds”的概念。我不会解释它,因为其他答案做得很好。
使用pragma once是一种简单而现代的方法,但是,包含保护以前使用过很多,并且是解决此问题的相对复杂的方法。

9nvpjoqh

9nvpjoqh6#

不知道它是什么时候被引入的,但是代替,例如:

#ifndef SOME_STRING_H
#define SOME_STRING_H

//...

#endif

因为我有时觉得SOME_STRING_H已经在我使用的库中定义了,或者在不同的命名空间中定义了完全相同的头,所以我更喜欢使用

#pragma once

// ...

// Nothing to put at the end like #endif above

相关问题