【问题标题】:Naming Include Guards命名包括警卫
【发布时间】:2011-06-19 13:09:19
【问题描述】:

C++ 包含守卫通常是如何命名的?我经常看到这个:

#ifndef FOO_H
#define FOO_H

// ...

#endif

但是,我认为这不是很直观。如果没有看到文件名,就很难分辨出FOO_H 的用途以及它的名称所指的含义。

什么是最佳实践?

【问题讨论】:

  • 虽然名称可能或多或少直观,但事实是,只要有一点经验,您就会停止阅读这些行。眼睛和大脑已经习惯了#ifdef blahblah...,而我几乎没有真正阅读过正在检查的内容,它是一个包含防护。
  • 对此有点有用的观点:stackoverflow.com/questions/1744144/…
  • 任何从事 C++ 开发的人最好很快习惯于识别标头保护。它将始终遵循您所看到的标准。 “最佳”实践(在引号中,因为它是必需的)是先放置 ifndef,紧接着定义,然后在文件末尾完成它。我建议你尽快学会识别这一点。

标签: c++ c-preprocessor include-guards


【解决方案1】:

我个人遵循 Boost 的建议。它可能是最大的高质量 C++ 库集合之一,而且它们没有问题。

它是这样的:

<project>_<path_part1>_..._<path_partN>_<file>_<extension>_INCLUDED

// include/pet/project/file.hpp
#ifndef PET_PROJECT_FILE_HPP_INCLUDED

这是:

  • 合法(注意以_[A-Z] 开头或包含__ 是不合法的)
  • 易于生成
  • 保证在项目中是唯一的(作为包含保护)(否则您在同一位置有两个文件)
  • 保证不用于其他任何用途(如果您以 INCLUDED 结束另一个宏,您就是在打架)

我读过关于 GUID 的文章,但那些看起来很奇怪。

显然我宁愿不是所有编译器都实现#pragma once(或者更好,#pragma multiple 和“一次”是默认行为......)

【讨论】:

  • 就我个人而言,我觉得添加扩展是多余的,但我喜欢 _INCLUDED 实际表达它代表什么的方式,+1 表示。我的偏好是使用 INCLUDE_GUARD_FOO(没有多余的 _H,毕竟我们只需要包含标题的守卫),这甚至更重要的是恕我直言,但这真的是一个品味问题。
  • @cmaster 添加 _H 在您有某些功能的 C 和 C++ 实现(分别使用 .h 和 .hpp 文件)的情况下会很有帮助。
【解决方案2】:

根据我自己的经验,约定是在包含它们的头文件之后命名包含保护,但名称全部大写并且句点替换为下划线。

所以test.h 变成了TEST_H

现实生活中的例子包括 Qt Creator,它在自动生成类头文件时遵循此约定。

【讨论】:

  • 最好只使用 FILENAME_H 作为包含保护名称,因为您将所有项目和所有库的所有文件保存在同一目录中,并且没有子目录,因此您知道它们永远不会有冲突的文件名。. .
  • 虽然这是一种常见做法,但它可能还不够好,具体取决于您的商店对 #defines 和其他名称的其他用途。
【解决方案3】:

直接取自google's style guide:

所有头文件都应该有#define 保护以防止多重包含。 符号名称的格式应 是 ___H_。到 保证唯一性,它们应该是 基于项目的完整路径 源树。例如,文件 项目 foo 中的 foo/src/bar/baz.h 应该有以下守卫:

 #ifndef FOO_BAR_BAZ_H_
 #define FOO_BAR_BAZ_H_
 ...
 #endif  // FOO_BAR_BAZ_H_

我在自己的项目中使用这种风格。

【讨论】:

  • 虽然一般来说 google 编码标准是我见过的最糟糕的标准之一,但我确实使用命名空间作为前缀。如果您在多个命名空间中有任何具有相同名称的东西,这是绝对必要的。
  • 我一直在想,后面的下划线有什么原因吗?
  • @Toby 只是为了让它(更多)独一无二......如果有人已经拥有CONFIG_H(例如包含的库),那么使用CONFIG_H_ 不会与之冲突。同样的原因有些人使用前导下划线,但他们不应该因为 thags 保留。
  • 链接已失效,现在更新为https://google.github.io/styleguide/cppguide.html
  • 我相信,这是一种更清晰的保护标头的方法,因为作为标头名称的注释(在#endif 指令之后)可以将其与其他常量区分开来。
【解决方案4】:

查看#include 是您的标头的代码。

如果是这样的:

#include "mylib/myheader.h"

mylib/myheader.h 已经是唯一的名称。只需大写并替换 / 和 。与_

#define MYLIB_MYHEADER_H

如果您的包含路径上有两个相对于包含路径具有相同名称的标头,则您已经在该级别发生了冲突。

【讨论】:

    【解决方案5】:

    FOO_H换成FOO_H_INCLUDED就更清楚了。

    【讨论】:

      【解决方案6】:

      正如其他人之前提到的,一个非常常见的约定是使用大写版本的名称,并将点替换为下划线:foo.h -> FOO_H

      但是,这可能会导致名称与简单和/或通用名称发生冲突。因此,非空 Visual C C++ 项目中的 stdafx.h 等自动生成的标头会附加一些随机字符串,例如:

      #ifndef FOO_H__NsknZfLkajnTFBpHIhKS
      #define FOO_H__NsknZfLkajnTFBpHIhKS
      #endif
      

      http://www.random.org/strings/ 是一个有用的随机生成器。

      另外,如果文件是某个子模块的一部分,或者它的内容位于一个特定的命名空间中,我也倾向于将其添加到保护中:

      #ifndef SOMECOMPONENT_FOO_H__NsknZfLkajnTFBpHIhKS
      #define SOMECOMPONENT_FOO_H__NsknZfLkajnTFBpHIhKS
      
      namespace somecomponent
      {
        ...
      }
      
      #endif
      

      【讨论】:

      • 这些是保留名称,因为有双下划线。所以使用它不是一个好习惯。
      【解决方案7】:

      我通常使用FOO_H_INCLUDED_ 之类的东西。一些 (Microsoft) 标头看起来很像 GUID 的字符串表示形式,但我从来不需要这么复杂的东西。

      【讨论】:

        【解决方案8】:

        通常人们通过文件名这样做,这样每个文件的代码只会被编译和添加一次。您可以随心所欲地制作 FOO_H,但几乎我曾经编码或看到的所有内容都使用了文件名。只要确保它是独一无二的,因为您不希望您的 FOO_H 与其他人的 FOO_H 冲突。

        【讨论】:

          【解决方案9】:

          我通常看现在几点了,然后把它附加到它的末尾,即FOO_H_248,这是一个额外的预防措施,你永远不必记住它,所以你不必担心事实上它很神秘。

          【讨论】:

          • 您使用的是 12 小时制还是 24 小时制,这是当地时间吗? ;-)
          • @T33C: 12? 24?你在说什么?这只是一个常规的 17 小时制。无论当时教皇在哪里,它都是当地时间。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-04-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多