多次包含相同的头文件会增加编译时间吗?
例如,假设我的项目中的每个文件都使用<iostream>
<string>
<vector>
和<algorithm>
。如果我在源代码中包含了很多文件,那么这会增加编译时间吗?
我一直认为防护标题是避免双重定义的重要目的,但作为副产品也消除了双重代码。
实际上,我认识的人提出了一些消除这种多重内容的想法。但是,我认为它们完全违背了c ++中的优秀设计实践。但是仍然想知道他可能提出这些变化的原因是什么?
答案 0 :(得分:11)
这些答案中的大多数都是错误的...对于现代编译器,假设标题使用通常的“包含守护”习惯,则多次包含同一文件会有零开销。
例如,GCC预处理器具有recognize the include guard idiom的特殊代码。它甚至不会打开第二个及后续#include
指令的头文件(更别读它)。
我不确定其他编译器,但如果大多数编译器没有实现相同的优化,我会感到非常惊讶。
答案 1 :(得分:1)
除了预编译头之外的另一种技术是编译器防火墙习惯用法,在此解释:
答案 2 :(得分:1)
每次#include <something.h>
出现在源文件中时,必须在include路径中找到'something.h'并读取。但是有#ifndef _SOMETHING_H_
检查,所以这些something.h的内容不会被编译。
因此有一些开销,但它确实很小。
答案 3 :(得分:1)
如果编译时间是一个问题,人们过去常常使用Praetorian推荐的优化,最初在Large Scale Software Design中推荐。但是,大多数现代编译器会自动针对此情况进行优化。例如,请参阅the help from gcc
答案 4 :(得分:0)
最好的方法是使用预编译的头文件。我不知道您使用的是哪种编译器,但大多数编译器都具有此功能。我建议你参考你的编译器手册来了解如何实现这一点。
它基本上收集所有头文件并将其编译成目标文件,然后链接器可以使用它。这加快了编译速度。
次要缺点:
你需要有1个“uberheader”,它包含在每个编译单元(.cpp)中。
在那个uberheader中,只包含来自库的静态头文件,而不是你自己的。然后编译器不需要经常重新编译它。
它有助于esp。当使用仅限标题的库时,例如boost或glm,eigen等。
HTH
答案 5 :(得分:0)
是的,多次包含相同的标头意味着需要在预处理器防护装置启动之前打开文件并防止多个定义。 Mozilla源代码使用以下技巧来防止这种情况:
foo.h中
#ifndef FOO_H
#define FOO_H
// whatever
#endif /* FOO_H */
在所有需要包含foo.h的文件中
#ifndef FOO_H
#include "foo.h"
#endif
这可以防止foo.h多次打开。当然,这取决于每个人遵循其预处理器防护的特定命名约定。
您不能使用标准标头执行此操作,因为它们的预处理器警卫没有通用的命名约定。
修改强>
在再次阅读您的问题后,我想您正在询问相同的标题是否包含在不同的源文件中。我上面谈到的内容并没有帮助。每个头文件仍然必须在每个翻译单元中打开并包含至少一次。我知道防止这种情况的唯一方法是使用预编译的头文件,如他的答案中提到的 @scorcher24 。但是我会远离这个解决方案,因为没有标准的方法可以在编译器之间生成预编译头,除非编译时间绝对过高。
答案 6 :(得分:0)
某些编译器,尤其是Microsoft的编译器,有一个#pragma once
指令,您可以使用该指令在已包含文件后自动跳过包含文件。这消除了任何性能损失。
答案 7 :(得分:0)
这可能是一个问题。正如其他人所说,大多数现代编译器
智能地处理案例,并且只会重新打开文件
堕落的病例。然而,大多数并不是全部,也是主要的一个
微软是个例外,很多人都不得不支持。该
最可靠的解决方案(如果这在您的环境中确实存在问题)是
使用Lakos惯例,把包括守卫围绕着
#include
以及标题中。当然,这意味着一个标准
生成警卫名称的惯例。 (对于外部包括,包裹
它们在您自己的标题中,尊重您的本地惯例。)
或者,您可以同时使用警卫和#pragma once
。该
警卫将永远工作,大多数编译器将避免额外的打开,
并且#pragma once
通常会避免与Microsoft的额外开放。
(#pragma once
无法在复杂的网络中可靠地实施
情况,但只要您的所有文件都在本地驱动器上,
它非常可靠。)