2014-12-02 47 views
7

我使用#pragma once(或者你使用include guardsàla #ifndef...)基本上在我的C++项目的每个头文件中。这是巧合,还是您在大多数开源项目中找到的例子(以避免依赖个人项目经验的依赖)。如果是这样,为什么不是另一种方式:如果我想多次包含头文件,我使用相同的特殊预处理器命令,如果没有,我将文件保持原样。为什么在C++中不包括警卫?

+8

'#pragma once'不是标准的; '#ifndef/#define/#endif'是。 – 2014-12-02 23:12:00

+3

@ T.C.好。但问题基本上保持不变。 – NOhs 2014-12-02 23:15:00

+0

你可能想看看''(和'')。如果'#pragma once'或类似的东西是自动的,那么两个标题将会被削弱。 (虽然它仍然会反对让正常的事情变得简单和短暂,而不会让不寻常的方式变得不可能,我的意思是,可以包括'#pragma redo'或其他类似的东西。) – Deduplicator 2014-12-02 23:16:57

回答

7

C++编译器的行为是根据它如何处理每个翻译单元来指定的。翻译单元是预处理器运行之后的单个文件。事实上,我们有一个在某些文件中收集声明并将其称为“头文件”文件的约定对编译器或C++标准没有任何意义。

简而言之,标准没有提供“头文件”,所以它不能提供自动包含头文件的保护。该标准仅提供预处理器指令#include,其余仅仅是约定。没有什么能够阻止你从前向声明所有内容,而不是使用头文件(除了怜悯谁应该维护那些代码......)。

所以,头文件并不是特殊的,有没有办法说“这是一个头文件,保护它”,但为什么我们不能防范是获得#include“d一切吗?因为#include比其他语言的模块系统强大得多。 #include会导致预处理器粘贴到其他文件中,而不一定是头文件。如果在不同文件中的一堆不同的命名空间中使用相同的using和typedef声明,有时候这会很方便。您可以将它们收集到一个文件中,并在几个地方将它们收集起来。你不希望自动包含防护措施阻止你这样做。

使用#ifndef#define来有条件地包括标题也仅仅是约定。该标准没有“包含警惕”的概念。 (现代编译但实际上都意识到包括警卫。认识到包括警卫可以允许更快的编译,但它无关,与正确地实现标准。)

获取迂腐

标准不自由的使用这个词“头文件”,特别是关于C和C++标准库。但是,#include的行为在§ 16.2 *Source file* inclusion(emph。mine)下定义,并且它不会为头文件授予任何特殊权限。

我们正在努力将合适的模块系统纳入C++标准。

+1

有“标题”,有“源文件”。 “标题”不需要是实际的文件。 – 2014-12-02 23:50:48

+0

真的吗?一定要告诉。那是因为标准允许任何实现定义的方式提供<>中的东西吗? – Praxeolitic 2014-12-02 23:51:38

+4

正确(或未指定,而不是实现定义)。所有这一切都需要编写#include 会导致适当的声明和定义被看到。编译器可以进行特殊处理来识别标准头名称并将其内容硬编码到它中,或者它可以从对象数据库中提取一大块AST数据。或者它可以让预处理器在文本上包含文件(这是所有常见实现在实践中所做的)。 – 2014-12-03 00:05:27

4

因为歇斯底里的葡萄干。

C++预处理程序与C预处理程序几乎相同,C预处理程序是在40多年前设计的。当时的编译器要简单得多。预处理器甚至更简单,只是一个愚蠢的宏处理器,甚至不是编译器。虽然C++标准没有指定它如何用于标准头文件,但概念上#include仍然是40年前的版本:它使预处理器将指定文件的内容插入到包含文件中。

在一个简单的20世纪70年代的C代码库中,没有大量的相互依赖关系和子模块,可能不需要包含防护。早期的预标准C没有使用函数原型,这是目前大多数包含文件的使用。如果包含头两次导致错误,您可能可以重新安排代码以防止其包含两次。

随着代码库的发展和变得越来越复杂,我认为偶然包含头两次(可能间接通过其他头文件)的问题变得更为常见。一种解决方案是改变预处理器,使其变得更加智能化,但这需要每个人都使用新的预处理器,并且会让它变得更大更慢。因此,开发了一种可以利用预处理器(#define#ifndef)的现有功能解决问题的约定,而不是通过防止两次包含头部,而是通过简单地将头部包括两次头部而无害化,因为它在第一次后没有效果它包括在内的时间。

随着时间的推移,这个约定变得更加广泛,现在几乎是通用的,除了罕见的包含两次头文件的例子,并被设计为以这种方式正确工作(例如<assert.h>)。

再以后,#pragma once被一些编译器作为替代推出,不可移植的办法有同样的效果,包括警卫,但当时有成千上万的各种C语言预处理器的副本中使用这个词四周,包括警卫已成为常态。

因此,目前的行为几乎肯定是由于历史原因。今天编写的现代语言,对于当今功能强大的计算机,如果从头开始设计,就不会使用类似C预处理器的东西。但C并不是在21世纪设计的。我认为随着时间的推移,包含守卫会议会慢慢建立起来,并且不需要对现有软件进行任何更改就能使其工作。现在改变它会破坏依赖于当前行为的未知数量的C和C++代码,并且可能不是一种选择,因为向后兼容性对于C和C++都很重要。

0

我不得不同意,主要因素是历史,偶尔你会看到依赖它们的代码不在那里。 MAME就是一个例子:它通过多次包含宏并且以不同的方式定义宏来从可读的基于宏的文件构建复杂的数据结构(或至少上次我查看,前一段时间)。如果包括守卫是自动的,你会遇到需要一种方法来关闭它们的代码。