如果我在文件中包含iostream或任何其他头文件两次,会发生什么?我知道编译器不会抛出错误。代码会被添加两次还是内部会发生什么?当我们包含头文件时,实际上会发生什么?
iostream
aelbi1ox1#
Include guard防止编译器实际看到文件的内容两次。Include guard基本上是一组位于头文件开头和结尾的预处理器条件指令:
#ifndef SOME_STRING_H #define SOME_STRING_H //... #endif
现在,如果你两次包含这个文件,那么第一次宏SOME_STRING_H没有定义,因此文件的内容被编译器处理和看到。但是,由于#ifdef之后的第一个是#define,因此定义了SOME_STRING_H,并且编译器在下一轮不会看到头文件的内容。为了避免冲突,在包含保护中使用的宏的名称依赖于头文件的名称。
SOME_STRING_H
#ifdef
#define
hgb9j2n62#
头文件是简单的野兽。当你#include <header>时,所发生的一切就是header的内容基本上被复制粘贴到文件中。为了防止头文件被多次包含,使用了include guards,这就是为什么在大多数头文件中,您会看到类似于
#include <header>
header
include guards
#ifndef SOME_HEADER_FILE_GUARD #define SOME_HEADER_FILE_GUARD //Contents of Header #endif
pcrecxhr3#
这取决于。除了<assert>之外,标准要求标准头的第二个(以及以后的)包含是无操作的。然而,这是标题的一个特征;编译器将(至少在概念上)在每次遇到include时读取并包括所有的头文本。在这种情况下避免多个定义的标准做法是使用include guards:头文件中的所有C++代码都将被包含在类似于以下内容中:
<assert>
#ifndef SPECIAL_NAME #define SPECIAL_NAME // All of the C++ code here #endif SPECIAL_NAME
显然,每个头需要一个不同的名称。在应用程序中,通常可以根据文件名和位置建立约定;比如subsystem_filename,Map了C符号中不法律的字符(如果你在文件名中使用它们)(而且通常都是大写的)。对于图书馆来说,最好的做法是生成一个合理长度的随机字符序列;更常见的是(尽管从实现质量的观点来看肯定是较差的)确保每个这样的符号开始都有文档化的前缀。当然,系统库可以使用保留符号(例如,以下划线开头,后跟大写字母的符号),以保证没有冲突。或者它可以使用一些完全不同的、依赖于实现的技术。例如,微软使用编译器扩展#pragma once; g的用法包括总是以_GLIBCXX开头的保护(在用户代码中这不是一个法律的符号)。这些选项不一定对您可用。
subsystem_filename
#pragma once
_GLIBCXX
1cklez4t4#
它只是被跳过,因为预处理器代码沿着以下行沿着:
#ifndef MY_HEADER_H #define MY_HEADER_H <actual header code here> #endif
因此,如果你包含两次,那么MY_HEADER_H已经定义,#ifndef和#endif之间的所有内容都被预处理器跳过。
MY_HEADER_H
#ifndef
#endif
sdnqo3pr5#
当你包含一个头文件时,它的所有内容都会被复制到“#include”指令所在的行。这是由预处理器完成的,是编译过程中的一个步骤。此过程对于标准文件(如iostream或存储在本地目录中的用户创建的“.h”文件)是相同的。但是,语法略有不同。对于存储在库中的“iostream”等文件,我们使用#include <filename>。而对于本地目录中的头文件,我们使用#include "filename.h"。
#include <filename>
#include "filename.h"
现在,如果我们包含两次头文件:
理想情况下,内容应该复制两次。但是...1.许多头文件使用提到#pragma once的现代实践,这指示预处理器只复制内容一次,无论头文件被包含多少次。1.一些非常古老的代码使用了一个称为“包含gauds”的概念。我不会解释它,因为其他答案做得很好。使用pragma once是一种简单而现代的方法,但是,包含保护以前使用过很多,并且是解决此问题的相对复杂的方法。
pragma once
9nvpjoqh6#
不知道它是什么时候被引入的,但是代替,例如:
因为我有时觉得SOME_STRING_H已经在我使用的库中定义了,或者在不同的命名空间中定义了完全相同的头,所以我更喜欢使用:
#pragma once // ... // Nothing to put at the end like #endif above
6条答案
按热度按时间aelbi1ox1#
Include guard防止编译器实际看到文件的内容两次。
Include guard基本上是一组位于头文件开头和结尾的预处理器条件指令:
现在,如果你两次包含这个文件,那么第一次宏
SOME_STRING_H
没有定义,因此文件的内容被编译器处理和看到。但是,由于#ifdef
之后的第一个是#define
,因此定义了SOME_STRING_H
,并且编译器在下一轮不会看到头文件的内容。为了避免冲突,在包含保护中使用的宏的名称依赖于头文件的名称。
hgb9j2n62#
头文件是简单的野兽。当你
#include <header>
时,所发生的一切就是header
的内容基本上被复制粘贴到文件中。为了防止头文件被多次包含,使用了include guards
,这就是为什么在大多数头文件中,您会看到类似于pcrecxhr3#
这取决于。除了
<assert>
之外,标准要求标准头的第二个(以及以后的)包含是无操作的。然而,这是标题的一个特征;编译器将(至少在概念上)在每次遇到include时读取并包括所有的头文本。在这种情况下避免多个定义的标准做法是使用include guards:头文件中的所有C++代码都将被包含在类似于以下内容中:
显然,每个头需要一个不同的名称。在应用程序中,通常可以根据文件名和位置建立约定;比如
subsystem_filename
,Map了C符号中不法律的字符(如果你在文件名中使用它们)(而且通常都是大写的)。对于图书馆来说,最好的做法是生成一个合理长度的随机字符序列;更常见的是(尽管从实现质量的观点来看肯定是较差的)确保每个这样的符号开始都有文档化的前缀。当然,系统库可以使用保留符号(例如,以下划线开头,后跟大写字母的符号),以保证没有冲突。或者它可以使用一些完全不同的、依赖于实现的技术。例如,微软使用编译器扩展
#pragma once
; g的用法包括总是以_GLIBCXX
开头的保护(在用户代码中这不是一个法律的符号)。这些选项不一定对您可用。1cklez4t4#
它只是被跳过,因为预处理器代码沿着以下行沿着:
因此,如果你包含两次,那么
MY_HEADER_H
已经定义,#ifndef
和#endif
之间的所有内容都被预处理器跳过。sdnqo3pr5#
当你包含一个头文件时,它的所有内容都会被复制到“#include”指令所在的行。这是由预处理器完成的,是编译过程中的一个步骤。
此过程对于标准文件(如iostream或存储在本地目录中的用户创建的“.h”文件)是相同的。但是,语法略有不同。
对于存储在库中的“iostream”等文件,我们使用
#include <filename>
。而对于本地目录中的头文件,我们使用#include "filename.h"
。现在,如果我们包含两次头文件:
理想情况下,内容应该复制两次。但是...
1.许多头文件使用提到
#pragma once
的现代实践,这指示预处理器只复制内容一次,无论头文件被包含多少次。1.一些非常古老的代码使用了一个称为“包含gauds”的概念。我不会解释它,因为其他答案做得很好。
使用
pragma once
是一种简单而现代的方法,但是,包含保护以前使用过很多,并且是解决此问题的相对复杂的方法。9nvpjoqh6#
不知道它是什么时候被引入的,但是代替,例如:
因为我有时觉得
SOME_STRING_H
已经在我使用的库中定义了,或者在不同的命名空间中定义了完全相同的头,所以我更喜欢使用: