17

如果我iostream在我的文件中包含两次或任何其他头文件会怎样?我知道编译器不会抛出错误。

代码会被添加两次还是内部会发生什么?

当我们包含头文件时实际发生了什么?

4

5 回答 5

23

包含保护防止文件的内容被编译器实际看到两次。

包含保护基本上是一组预处理器的条件指令,位于头文件的开头和结尾:

#ifndef SOME_STRING_H
#define SOME_STRING_H

//...

#endif 

SOME_STRING_H现在,如果您两次包含该文件,则未定义第一次循环宏,因此编译器会处理和查看文件的内容。#ifdef但是,由于定义了is之后的第一件事#defineSOME_STRING_H并且下一轮编译器看不到头文件的内容。

为了避免冲突,包含保护中使用的宏的名称取决于头文件的名称。

于 2012-10-17T07:09:12.997 回答
4

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

#ifndef SOME_HEADER_FILE_GUARD 
#define SOME_HEADER_FILE_GUARD

//Contents of Header

#endif
于 2012-10-17T07:11:23.253 回答
2

由于以下几行的预处理器代码,它只是被跳过了:

#ifndef MY_HEADER_H
#define MY_HEADER_H

<actual header code here>

#endif

因此,如果您包含两次,则 thenMY_HEADER_H已经定义,并且预处理器会跳过#ifndefand之间的所有内容。#endif

于 2012-10-17T07:10:14.513 回答
2

这取决于。除了 之外<assert>,该标准要求标准标头的第二个(和更高版本)包含为无操作。然而,这是标题的一个特征;每次遇到包含时,编译器都会(至少在概念上)读取并包含所有标题文本。

在这种情况下避免多重定义的标准做法是使用包含保护:标头中的所有 C++ 代码都将包含在如下内容中:

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

显然,每个标题都需要不同的名称。在应用程序中,您通常可以根据文件名和位置建立约定;之类subsystem_filename的,在 C++ 符号中使用不合法的字符(如果您在文件名中使用它们)映射(并且通常所有内容都大写)。对于图书馆来说,最好的做法是生成一个相当长的随机字符序列;更频繁(尽管从实现质量的角度来看肯定较差)是确保每个这样的符号都以记录的前缀开头。

当然,系统库可以在此处使用保留符号(例如,以下划线后跟大写字母的符号),以保证没有冲突。或者它可以使用一些完全不同的、依赖于实现的技术。例如,微软使用编译器扩展#pragma once;g++ 使用包含始终以开头的守卫_GLIBCXX(这不是用户代码中的合法符号)。这些选项不一定对您可用。

于 2012-10-17T08:51:58.863 回答
0

包含头文件时,其所有内容都会复制到放置“#include”指令的行。这是由预处理器完成的,是编译过程中的一个步骤。

对于存储在本地目录中的标准文件(如 iostream)或用户制作的“.h”文件,此过程是相同的。但是,语法略有不同。

我们#include <filename>用于存储在库中的“iostream”之类的文件。而对于本地目录中的头文件,我们使用#include "filename.h".

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

理想情况下,内容应该被复制两次。但...

  1. 许多头文件使用提到的现代做法,#pragma once它指示预处理器只复制一次内容,无论头文件包含多少次。

  2. 一些非常古老的代码使用了一个名为“include gaurds”的概念。我不会解释它,因为其他答案做得很好。

使用pragma once是一种简单且现代的方法,但是,包含防护以前使用过很多,并且是解决此问题的相对复杂的方法。

于 2020-09-19T06:13:07.820 回答