除了包含保护之外,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】:
我认为这个问题本身就有缺陷。术语包含警卫指的是#define
s,并在防止多重包含的特定用途中检查#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++ 的有效用途还包括保护?的主要内容,如果未能解决你的问题,请参考以下文章