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