关于包含带有警卫的头文件

时间:2013-07-23 08:50:36

标签: c++

我正在读一本关于Applied C ++的书。

  

包含防护将阻止更多地包含头文件   在编译源文件期间不止一次。你的符号名称   应该是唯一的,我们建议根据名称选择名称   的文件。例如,我们的文件cache.h包含这个包含   后卫。

#ifndef _cache_h_
 #define _cache_h_
 ...
 #endif // _cache_h_
  

Lakos描述了使用冗余包含警戒来加速   汇编。见[Lakos96]。对于大型项目,打开需要一些时间   每个文件,只发现包含保护符号已经存在   已定义(即文件已包含在内)。对...的影响   编译时间可能是戏剧性的,Lakos显示可能的20倍   当只有标准包括警卫时,编译时间会增加   使用

     

[Lakos96]:LargeScale C ++软件设计。

我没有Lakos96参考书来提及概念,所以在这里寻求帮助。

我对上述文字的疑问是

  1. 作者的意思是“对于大型项目,打开每个文件需要花费时间,但却发现已经定义了包含保护符号”?

  2. 作者的意思是“当使用标准包括警卫时”?

  3. 感谢您的时间和帮助。

6 个答案:

答案 0 :(得分:8)

来自C ++编码标准(Sutter,Alexandrescu)

  

许多现代C ++编译器自动识别标题保护(参见   项目24)并且甚至不打开两次相同的标题。有些还提供   预编译的头文件,有助于确保经常使用的,   经常不会解析很少更改的标题

所以,我会认为这些建议已经过时了(除非你还在使用一些非常过时的编译器)。

关于你的问题:

  1. 这意味着:打开一个不需要的文件(因为它已经被包含在内;你知道因为已经定义了包含保护),这是一个很好的;这可能是一个问题,如果你这么做很多次(如果你的项目中有数百个文件就会发生这种情况)。
  2. 与使用非冗余编译保护相反。
  3. 什么是冗余编译保护?

      

    天真的编译器会在每次包含文件时重新加载文件。至   避免这种情况,将RedundantIncludeGuards放在include:header.h

    周围
     #ifndef HEADER_H_
      #define HEADER_H_
      // declarations
      #endif
    
      

    foo.c的

     #ifndef HEADER_H_
      #include "header.h"
      #endif
    

    了解更多here。您的参考声称通过这样做,您可以在编译期间比foo.c仅执行时快20%

     #include "header.h"
    

答案 1 :(得分:6)

我不知道Lakos96说的是什么,但无论如何我都要猜测......

标准包括警卫就像:

foo.h中

#ifndef FOO_H_INCLUDED
#define FOO_H_INCLUDED
....
#endif

冗余include guard在包含文件时使用宏:

bar.c

#ifndef FOO_H_INCLUDED 
#include "foo.h"
#endif

这样第二次包含foo.h文件时,编译器甚至不会在磁盘中搜索它。因此加速:想象一个大项目,一个单独的编译单元可能包括foo.h 100次,但只有第一个将被解析。其他99次将被预编译器搜索,打开,标记化,丢弃并关闭。

但请注意那是在1996年。今天,GCC提供了一个众所周知的例子,它具有特定的优化功能,可识别包含保护模式,并使冗余包括保护,嗯......,冗余。

答案 2 :(得分:5)

拉科斯的书很老了。这可能是真的一次,但你应该在你的机器上计时。许多人现在不同意他,例如 http://www.allankelly.net/static/writing/overload/IncludeFiles/AnExchangeWithHerbSutter.pdfhttp://c2.com/cgi/wiki?RedundantIncludeGuardshttp://gamearchitect.net/Articles/ExperimentsWithIncludes.html

  

Herb Sutter,C ++大师,现任ISO C ++标准主席   委员会,反对外部包括警卫:

     “顺便说一句,我强烈反对拉科斯的外部包括警卫   基于两个理由:

     
      
  1. 大多数编译器都没有任何好处。我承认我还没有做过测量,因为当时Lakos似乎做过,但就我而言   知道今天的编译器已经有了智能,以避免构建时间   重读开销 - 甚至MSVC都做了这个优化(虽然它   要求你说“#pragma once”),它是最弱的编译器   很多方式。

  2.   
  3. 外部包含守卫违反封装,因为它们需要许多/所有呼叫者知道标头的内部 - 在   特别是,用作守卫的特殊#define名称。他们也是   脆弱 - 如果你的名字错了怎么办?如果名称改变怎么办?“

  4.   

答案 3 :(得分:1)

在一个大型项目中,可能有许多标题 - 可能是100个甚至1000个文件。在正常情况下,每个标题内都包含保护,编译器必须检查(但见下文)文件的内容,看它是否已被包含。

标题内的这些警卫是“标准”。

Lakos建议(对于大型项目)将守卫放在#include指令周围,这意味着如果已经包含了标题,则甚至不需要打开标题。

据我所知,所有现代C ++编译器都支持#pragma once指令,它与预编译的头文件相结合意味着在大多数情况下问题不再是问题。

答案 4 :(得分:0)

我认为它所指的是在头文件之外复制包含保护,例如

#ifndef _cache_h_
#include <cache.h>
#endif

但是,如果这样做,您将不得不考虑标题保护有时会在文件中发生变化。你肯定不会在现代系统中看到20倍的改进 - 除非你的所有文件都在一个非常远程的网络驱动器上 - 但是你可以通过将项目文件复制到本地驱动器来获得更好的改进! / p>

前面有一个类似的问题,关于“包括冗余文件”(指多次包含头文件),我构建了一个包含30个源文件的小型系统,包括<iostream>“不必要”,编译时间的整体差异在包括<iostream>和不包括{{1}}之间为0.3%。我相信这一发现表明GCC的改进是“自动识别不包含任何内容的文件,包括警卫”。

答案 5 :(得分:0)

  1. 在人数较多的大型项目中,可能会有一个处理时间转换的模块,作者可以选择使用TIME作为后卫。然后你将有另一个处理精确计时,它的作者,不知道第一个,也可以选择TIME。现在你发生了冲突。如果他们使用TIME_TRANSFORMATIONPRECISE_TIMING_MODULE,他们就可以了

  2. 不知道。我猜它可能意味着“当你每次都这样做时,它始终是你的编码标准”。