除了包含保护之外,C++ 的有效用途还包括保护?

Posted

技术标签:

【中文标题】除了包含保护之外,C++ 的有效用途还包括保护?【英文标题】:Valid uses for C++ include guards besides, well, include-guarding? 【发布时间】:2011-02-01 21:36:34 【问题描述】:

This question 是几个讨论 C++ 命名约定的其中之一,包括守卫。问这个问题的人认为这个命名约定:

#ifndef FOO_H
#define FOO_H

// ...

#endif

单独使用时有点不直观(FOO_H 是什么意思?),我倾向于同意。

其他人说,除非需要添加更多内容以更好地避免碰撞(例如PROJECTNAME_FOO_H_SDFFGH69876GF),否则FOO_H 这个名称就可以了,因为从它的上下文中可以清楚地看出它的目的是什么(即,它在开头的同名文件,很明显它是一个包含保护)。

如果拥有FOO_H 的唯一目的是防止多重包含,我可以购买此产品,但我是否希望在文件的其他位置拥有FOO_H?我认为条件编译是一个很好的理由,在这种情况下,将其命名为 FOO_H_INCLUDED 会更清楚。

是否有与此类似的直接用途,或者我应该避免重新利用包含守卫?

【问题讨论】:

我不会管它的。这感觉就像“如果它没有损坏,请继续修复它直到它损坏”类型的情况之一。 这就是为什么我更喜欢#pragma once @James:#pragma once 不允许您按照 OP 提及的方式进行有条件地编译。 @James:有些人更喜欢标准保证工作的解决方案。 @David:我想知道,像 #pragma once 这样的东西是为 C++0x 提议的吗? 【参考方案1】:

我认为这个问题本身就有缺陷。术语包含警卫指的是#defines,并在防止多重包含的特定用途中检查#defined,这可以说是唯一的用途。

现在更笼统地说,定义和条件编译可用于其他事情,例如编写与平台相关且仅在某些情况下才会编译的代码...

FOO_H 还是FOO_H_INCLUDED 哪个更好,我会一直说后者最有表现力,然后我会回到vi 并在我的下一个foo.h 标题中输入FOO_H。同样,正如我在对另一个问题的评论中提到的那样,您已经习惯了这种模式:

#ifndef XXXXXXXXX
#define XXXXXXXXX

#endif

作为文件中的前两行和最后一行,您最终会注意到。直到如果您重复使用相同的名称,它就会反咬......

【讨论】:

【参考方案2】:

有时,我将它用于“实现”头文件。守卫的实际命名方案可能不同。

#ifndef FOO_IMPL_H
#define FOO_IMPL_H

#ifndef FOO_H
#error Don't include this directly
#endif

// Ugly implementation of the stuff in foo.h, with an uncountable
// number of template and typename keywords

#endif 

【讨论】:

这仍然不能阻止 #include "foo.h" 后跟 #include "foo_impl.h"。如果您真的想防止这种情况发生,还有更好的方法,例如 Boost 如何始终使用“细节”来标记实现细节和约定(无需技术强制),用户应该忽略幕后的那个人。【参考方案3】:

在我看来,包含警卫应该是包含警卫,仅此而已。如果你想要条件编译,我会定义别的东西。如果您在代码中乱扔#if defined(FOO_H),我想人们会觉得这很奇怪,因为_H 通常表示包含保护。

我能想到的一件事是检查文件是否已被包含(呃!),这样您就不必自己包含文件。与前向声明或 #pragma once

相比,我不确定这是否会加快编译速度

在 main.cpp 中:

#include "bar.h" // comment this line to see the difference
#include "foo.h"

int main()

    return 0;

在 bar.h 中:

#ifndef BAR_H
#define BAR_H

class BarClass

;

#endif

在 foo.h 中:

#ifndef FOO_H
#define FOO_H

#if !defined(BAR_H)
#error bar h was not included // forgot how to do compiler warnings...
#include "bar.h" // we should include the file
#else
#error bar h was included // same thing, should be changed to warning
#endif

// do something with BarClass

#endif FOO_H

【讨论】:

编译器可以缓存包含保护以跳过实际读取文件而无需使用 #pragma 一次。【参考方案4】:

我认为条件编译是一个很好的理由,在这种情况下,将其命名为 FOO_H_INCLUDED 会更清楚。

我在这里只看到一个非常狭窄的用法,以避免在您只需要前向声明时包含标题,但如果之前已包含标题,则希望跳过前向声明。

然而,这不是当今编译 C++ 的瓶颈,而且总是有前向声明或总是包含头文件的额外混淆是不值得的。

即使你需要大量的——我的意思是成百上千行这些前向声明——这样它确实变得有价值,你最好使用一个类似于 的专用标题,而不是维护这个在多个地方。

【讨论】:

【参考方案5】:

重新调整用途:除非文件的全部内容都被 ifndef/define/endif 包围,否则 FOO_H 并不是真正的 文件 包含保护,它只是保护文件的一部分.所以可能它不应该以整个文件命名。递归包含同一文件是可能出现这种情况的一种情况。

也就是说,我怀疑即便如此,定义的用途应该很明显(无论如何,更明显的是,无论你在做什么棘手的事情都不简单,包括守卫)。无论您在哪里看到 ifndef FOO/define FOO 模式,或者甚至只是 ifndef FOO/define 很多东西,一个简单的注释都可以使 FOO 的含义变得明显(如果它还没有的话)。

如果问题的任何部分是令牌FOO_H 是否可能用于其他目的:我想如果你有一个名为uppercase.h 的文件使用UPPERCASE_H 作为包含保护,那么这可能可以想象与某人的rot13.h 发生冲突,包含#define UPPERCASE_A ('N') 等。愚蠢的例子,但如果你在头文件中定义字母的属性,那么认为其中一些可能以_H 结尾并不可笑。

更现实地说,我有一些项目,其中包含文件在不同目录中具有相同的基本名称。

因此,无论上下文和重新利用问题如何,我都不会使用 FOO_H 作为包含保护。

【讨论】:

以上是关于除了包含保护之外,C++ 的有效用途还包括保护?的主要内容,如果未能解决你的问题,请参考以下文章

(交叉编译)平台文件是不是需要包含保护?

绕过错误 C2248“无法访问在类中声明的受保护成员”的有效方法

c++面向对象三大特征封装继承和多态知识总结

邮件安全之邮件加密初探

精密进近保护区的显示

精密进近保护区的显示