我以前使用以下代码来确保包含文件不会多次加载。
#ifndef _STRING_
#include <string>
#endif
// use std::string here
std::string str;
...
这个技巧在“API Design for C ++”一书中有所说明。
现在我的合作告诉我,在Visual Studio中这不是必需的,因为如果字符串的实现头文件包含#pragma once
,则不需要包含保护来提高编译速度。
这是对的吗?
从原书中引用:
7.2.3 Redundant #include Guards
Another way to reduce the overhead of parsing too many include files is to add redundant preprocessor
guards at the point of inclusion. For example, if you have an include file, bigfile.h, that looks
like this
#ifndef BIGFILE_H
#define BIGFILE_H
// lots and lots of code
#endif
then you might include this file from another header by doing the following:
#ifndef BIGFILE_H
#include "bigfile.h"
#endif
This saves the cost of pointlessly opening and parsing the entire include file if you’ve already
included it.
答案 0 :(得分:5)
通常,“包含守卫”一词意味着此#ifdef
,#define
,#endif
序列围绕特定头文件内部的内容展开文件。
许多C ++编译器提供#pragma once
语句,以保证外部的相同行为。但是为了便携式C / C ++代码,我不鼓励使用它。
更新(根据OP的编辑)
另外,将#ifdef
,#endif
放在另一个文件中的#include
语句可能会阻止预处理器打开包含文件本身(从而减少编译时间和内存使用量)。我希望#pragma once
会自动执行此操作,但无法确定(这可能是特定于实现的)。
答案 1 :(得分:2)
你曾经不需要这样做,因为有能力的开发人员编写的任何头文件都有自己的后卫。您可以假设标准库标题是由有能力的工程师编写的,如果您发现自己使用第三方标题但没有包含警卫......那么,第三方现在非常怀疑......
至于编写自己的标题,可以使用标准:
#ifndef MY_HEADER_H
#define MY_HEADER_H
// ...code
#endif
或者只是使用:
#pragma once
请注意,这不是标准的C或C ++,而是编译器扩展。它不适用于每个编译器,但使用它是您的决定,取决于您的预期用途。
答案 2 :(得分:2)
冗余包括警卫,根据定义&#34;冗余&#34;。它们不会影响通过编译创建的二进制文件。但是,他们确实有一个好处。冗余包括防护可以减少编译时间。
谁在乎编译时间?我在乎。我只是一个开发人员,是一个由数百名开发人员组成的项目,拥有数百万行源代码,包含数千个源文件。完成项目的重建需要45分钟。从版本控制拉动增量构建需要20多分钟。由于我的工作取决于这个大项目,我在等待这个长期构建时无法执行任何测试。如果将构建时间缩短到5分钟以下,我们公司将受益匪浅。假设构建时间节省了20分钟。 1年* 100位开发人员* 1次建造/天,* 1/3小时/建造* 250天/年* 50美元/小时=每年节省416,667美元。有人应该关心这一点。
对于Ed S,我使用Redundant Include守卫已经有10年了。偶尔你会找到一个使用这种技术的人,但最害羞的是因为它可以制作丑陋的代码。 &#34; #pragma一次&#34;肯定看起来更干净。百分比方面,很少有开发人员通过继续他们的教育和技术不断尝试提高他们的才能。冗余的#include防护技术有点模糊,只有当有人不愿对大型项目进行分析时才能实现其优势。你知道有多少开发人员不顾一切地购买高级技术的C ++书籍?
回到关于Redundant的原始问题在Visual Studio中包含一次保护vs #pragma ...根据Wiki #pragma once,编译器支持#34; #pragma一次&#34;可能更有效率#include警卫,因为他们可以分析文件名和路径,以防止加载已加载的文件。名称中提到了三个编译器进行了这种优化。 Visual Studio显然没有出现在这个列表中。因此,我们仍然想知道在Visual Studio中是否应该使用多余的#include防护,或者#pragma一次。
对于中小型项目,#pragma once肯定很方便。对于大型项目,其中编译时间成为开发过程中的一个因素,冗余的#include保护可以让开发人员更好地控制编译过程。任何管理或设计大型项目的人都应该在他们的图书馆中有Large Scale C++ Design - 它会讨论并建议多余的#include警卫。
可能比冗余更大的好处包括警卫聪明地使用#includes。随着C ++模板和STL变得越来越流行,方法实现正在从.cpp文件迁移到.h文件。 .cpp实现可能具有的任何头依赖项,现在必须迁移到.h文件。这会增加编译时间。我经常看到开发人员在他们的头文件中堆叠了许多不必要的#include,因此他们不必费心去识别他们实际需要的头文件。这也增加了编译时间。
答案 3 :(得分:0)
#pragma once
是一个包含守卫的更好形式。如果您使用它,则不需要基于#define
的包含保护。
一般来说,这是一种更好的方法,因为它可以防止名称冲突破坏包含守卫。
话虽如此,include guard应该在头文件中,而不是包含include。包装内容应该是完全没必要的(而且可能会让其他人感到困惑)。
编辑:
我认为我们正在谈论两件不同的事情。我的问题是,当我们使用预先存在的头文件时,我们是否应该使用包含#scragma once或#ifndef xxx
在那种情况下,没有。如果标题有适当的保护,则没有理由避免包含它。这只会增加混乱和复杂性。
答案 4 :(得分:0)
这不是包括使用警卫的方式。您不会将#include
包裹在包含警戒中。头文件应将自己的内容包装在include guard中。每当你编写一个可能包含在其他文件中的文件时,你应该这样做:
#ifndef _SOME_GUARD_
#define _SOME_GUARD_
// Content here
#endif
使用Visual Studio实现的C ++库,可以通过string
标题#pragma once
或检查#ifndef _STRING_
来完成。