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

ven*_*rty 6 c++

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

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

#ifndef _cache_h_
 #define _cache_h_
 ...
 #endif // _cache_h_
Run Code Online (Sandbox Code Playgroud)

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

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

我没有Lakos96参考书来引用概念,所以在这里寻求帮助.

我对上述文字的疑问是

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

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

谢谢你的时间和帮助.

Ste*_*sca 8

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

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

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

至于你的问题:

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

什么是冗余编译保护?

一个天真的编译器会在每次包含文件时重新加载该文件.为避免这种情况,请在include:header.h周围放置RedundantIncludeGuards

 #ifndef HEADER_H_
  #define HEADER_H_
  // declarations
  #endif
Run Code Online (Sandbox Code Playgroud)

foo.c的

 #ifndef HEADER_H_
  #include "header.h"
  #endif
Run Code Online (Sandbox Code Playgroud)

在这里阅读更多.您的参考声称,通过这样做,您在编译期间的速度可能比foo.c仅执行时快20%

 #include "header.h"
Run Code Online (Sandbox Code Playgroud)


rod*_*igo 6

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

标准包括警卫就像:

foo.h中

#ifndef FOO_H_INCLUDED
#define FOO_H_INCLUDED
....
#endif
Run Code Online (Sandbox Code Playgroud)

包含文件时,冗余包含Guard正在使用宏:

bar.c

#ifndef FOO_H_INCLUDED 
#include "foo.h"
#endif
Run Code Online (Sandbox Code Playgroud)

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

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


doc*_*ove 5

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

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

"顺便说一下,我强烈不同意拉科斯的外部包括两名警卫:

  1. 大多数编译器都没有任何好处.我承认我还没有做过测量,因为Lakos似乎已经做过了,但据我所知,今天的编译器已经有了智能,以避免重建开销的构建时间 - 即使MSVC做了这个优化(尽管它需要你说"#pragma once"),它在很多方面都是最弱的编译器.

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

  • http://tinodidriksen.com/2011/08/31/cpp-include-speed/用于速度测量,显示它今天根本不重要,如果有的话. (2认同)