3

我曾经使用以下代码来确保包含文件不会被多次加载。

#ifndef _STRING_
#include <string>
#endif

// use std::string here
std::string str;
...

这个技巧在“API Design for C++”一书中有说明。

现在我的同事告诉我,这在 Visual Studio 中是没有必要的,因为如果 string 的实现头文件包含#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.
4

5 回答 5

5

通常术语“包含守卫”意味着这个#ifdef, #define,序列被放置在这个文件#endif的特定头文件的内容周围。

许多 C++ 编译器提供了#pragma once保证外部相同行为的语句。但是为了可移植的 C/C++ 代码,我不鼓励使用它。

更新(根据 OP 的编辑)
另外将#ifdef,放在另一个文件中#endif的语句周围#include可能会阻止预处理器打开包含文件本身(从而稍微减少编译时间和内存使用)。我希望#pragma once会自动执行此操作,但不能确定(这可能是特定于实现的)。

于 2013-02-27T18:20:32.253 回答
2

永远不需要这样做,因为有能力的开发人员编写的任何头文件都会有自己的保护。您可以假设标准库头文件是由有能力的工程师编写的,如果您发现自己使用了没有包含保护的第三方头文件......好吧,现在该第三方被高度怀疑......

至于编写自己的标题,您可以使用标准:

#ifndef MY_HEADER_H
#define MY_HEADER_H

// ...code

#endif

或者只是使用:

#pragma once

请注意,这不是标准的 C 或 C++,它是编译器扩展。它不适用于所有编译器,但使用它是您的决定,取决于您的预期用途。

于 2013-02-27T18:15:41.103 回答
2

根据定义,冗余包括警卫是“冗余的”。它们不会影响通过编译创建的二进制文件。但是,它们确实有好处。冗余的包含守卫可以减少编译时间。

谁在乎编译时间?我在乎。我只是一名开发人员是一个由数百名开发人员组成的项目,在数千个源文件中拥有数百万行源代码。项目的完整重建需要我 45 分钟。从修订控制拉取增量构建需要 20 多分钟。由于我的工作依赖于这个大项目,所以在等待这个长时间的构建时我无法执行任何测试。如果将构建时间缩短到 5 分钟以下,我们公司将受益匪浅。假设构建时间节省了 20 分钟。1 年 * 100 名开发人员 * 1 次构建/天,* 1/3 小时/构建 * 250 天/年 * 50 美元/小时 = 每年节省 416,667 美元。应该有人关心这个。

对于 Ed S,我使用 Redundant Include 防护装置已有 10 年了。偶尔你会发现有人使用该技术,但最怕它,因为它可以生成难看的代码。“#pragma once”肯定看起来更干净。就百分比而言,很少有开发人员不断尝试通过继续他们的教育和技术来提高他们的才能。多余的#include 守卫技术有点晦涩难懂,只有当有人费心对大型项目进行分析时,它的好处才会显现出来。您知道有多少开发人员会特意购买有关高级技术的 C++ 书籍?

回到 Visual Studio 中关于 Redundant Include guards 与 #pragma once 的原始问题...根据 Wiki #pragma once,支持“#pragma once”的编译器可能比 #include 保护器更有效,因为它们可以分析文件名和路径以防止加载已加载的文件。三个编译器的名字被提到具有这种优化。此列表中明显没有的是 Visual Studio。所以,我们仍然想知道,在 Visual Studio 中,是否应该使用冗余的#include 保护,或者使用一次#pragma。

对于中小型项目,#pragma once 肯定很方便。对于在开发过程中编译时间成为一个因素的大型项目,冗余的#include 保护使开发人员可以更好地控制编译过程。任何管理或构建大型项目的人都应该在他们的库中包含大型 C++ 设计——它讨论并推荐冗余的#include 保护。

#includes 的巧妙使用可能比多余的包含守卫更大的好处。随着 C++ 模板和 STL 变得越来越流行,方法实现正在从 .cpp 文件迁移到 .h 文件。.cpp 实现所具有的任何头文件依赖项现在都必须迁移到 .h 文件。这会增加编译时间。我经常看到开发人员在他们的头文件中堆放了许多不必要的#include,这样他们就不必费心识别他们实际需要的头文件。这也增加了编译时间。

于 2015-09-16T17:19:24.000 回答
0

#pragma once是一种更好的包含守卫形式。如果使用它,则不需要基于#define.

一般来说,这是一种更好的方法,因为它可以防止名称冲突破坏包含保护。

话虽如此,包含保护应该在头文件中,而不是包装包含。包装包含应该是完全没有必要的(并且可能会混淆其他人)。


编辑:

我认为我们在谈论两件不同的事情。我的问题是,当我们使用具有 #pragma once 或 #ifndef xxx 的预先存在的头文件时,是否应该使用包含保护

在那种情况下,没有。如果标头有适当的保护,则没有理由尝试避免包含它。这只会增加混乱和复杂性。

于 2013-02-27T18:15:30.820 回答
0

这不是使用包含警卫的方式。您不会将#includes 包装在包含防护中。头文件应将其自己的内容包装在包含保护中。每当您编写可能包含在其他文件中的文件时,您应该这样做:

#ifndef _SOME_GUARD_
#define _SOME_GUARD_

// Content here

#endif

使用 Visual Studio 的 C++ 库实现,这可以通过string具有#pragma once或检查#ifndef _STRING_.

于 2013-02-27T18:15:57.687 回答