71

我已经审查了如何正确使用包含指令C++ #include 语义的问题,但都没有解决这个问题——当我输入标题时,SO 建议的其他问题也没有......

如果有的话,写作的好处是什么:

#include "../include/someheader.h"
#include "../otherdir/another.h"

与仅使用普通文件名相比:

#include "someheader.h"
#include "another.h"

或者可能是没有“ ..”的相对名称:

#include "include/someheader.h"
#include "otherdir/another.h"

我看到的问题是:

  • 您无法移动标题而不担心哪些源文件包含它。
  • 您最终可能会在依赖项和错误报告中获得非常长的标题路径。我今天有一个“ ../dir1/include/../../include/../dir2/../include/header.h”。

我能看到的唯一优点是,虽然您不需要移动文件,但您可能无需总是使用 ' -I' 指令来查找标题,但会失去灵活性,以及​​在 sub-sub 中编译的复杂性-目录等似乎超过了好处。

那么,我是否忽略了一个好处?


感谢您的投入。我认为共识是使用我忽略的“..”表示法没有任何重大好处。一般来说,我喜欢“somewhere/header.h”表示法;我确实在新项目中使用它。我正在研究的不是新的。

问题之一是存在各种标题集,通常带有前缀,例如rspqr.h, rsabc.h, rsdef.h, rsxyz.h。这些都与rsmp目录中的代码有关,但有些头文件在其中rsmp,有些在中心包含目录中,其中没有子目录等子目录rsmp。(并对代码的其他各个区域重复此操作;在多个位置有标头,其他代码位随机需要。)移动内容是一个主要问题,因为多年来代码变得如此复杂。-I并且生成文件在提供的选项方面并不一致。总而言之,这是一个几十年来不那么善意的忽视的悲惨故事。在不破坏任何东西的情况下修复这一切将是一项漫长而乏味的工作。

4

8 回答 8

48

我更喜欢路径语法,因为它非常清楚头文件属于哪个名称空间或模块。

#include "Physics/Solver.h"

非常自我描述,不需要每个模块将其名称作为头文件的前缀。

不过,我几乎从不使用“..”语法,而是让我的项目包含指定正确的基本位置。

于 2009-02-28T01:42:23.753 回答
28

问题#include "../include/header.h"在于它经常会意外工作,然后看似无关的更改会使其稍后停止工作。

例如,考虑以下源布局:

./include/header.h
./lib/library.c
./lib/feature/feature.c

假设您正在运行编译器,其包含路径为-I. -I./lib. 发生什么了?

  • ./lib/library.c可以做#include "../include/header.h",这是有道理的。
  • ./lib/feature/feature.c也可以#include "../include/header.h",虽然没有意义。这是因为编译器将尝试#include相对于当前文件位置的指令,如果失败,它将尝试相对于路径#include中每个-I条目的指令。#include

此外,如果您稍后-I./lib#include路径中删除,那么您会破坏./lib/feature/feature.c.

我发现以下内容更可取:

./projectname/include/header.h
./projectname/lib/library.c
./projectname/lib/feature/feature.c

除了 , 我不会添加任何包含路径条目-I.,然后两者都library.cfeature.c使用#include "projectname/include/header.h". 假设“projectname”可能是唯一的,这在大多数情况下不应导致名称冲突或歧义。如果绝对必要,您还可以使用包含路径和/或 make 的VPATH功能将项目的物理布局拆分到多个目录中(例如,以适应特定于平台的自动生成代码;当您使用 时,这确实会崩溃#include "../../somefile.h")。

于 2009-02-28T03:17:37.503 回答
10

#include ""在以下情况下,以一个或多个 " " 序列开始指令路径../

  • 您想包含一个文件,其与包含文件的搭配是固定的,并且
  • 您正在使用 POSIX 系统或 VC++ 构建,并且
  • 您希望避免关于将包含哪个文件的歧义。

总是很容易提供一个示例,说明您的代码库在哪里包含错误,并且这随后会导致难以诊断的故障。但是,即使您的项目没有故障,如果您依赖绝对路径来指定相对于彼此定位的文件,它也可能被第三方滥用。

例如,考虑以下项目布局:

./your_lib/include/foo/header1.h
./your_lib/include/bar/header2.h
./their_lib/include/bar/header2.h

your_lib/include/foo/header1.h应该如何包含your_lib/include/bar/header2.h?让我们考虑两个选项:

  1. #include <bar/header2.h>

    假设your_lib/includetheir_lib/include都被引用为头文件搜索路径(例如使用 GCC-I-isystem选项),那么选择哪个header2.h对搜索这两个路径的顺序很敏感。

  2. #include "../bar/header2.h"

    编译器将搜索的第一个位置是your_lib/include/foo/header1.h的位置,即your_lib/include/foo/。它将首先尝试your_lib/include/foo/../bar/header2.h,它会减少到your_lib/include/bar/header2.h,它会在其中找到正确的文件。根本不会使用标题搜索路径,并且几乎没有歧义的余地。

在这种情况下,出于给出的原因,我强烈推荐选项 2)。

针对其他答案中的一些论点:

  • @andrew-grant

    ...它非常清楚头文件属于哪个名称空间或模块。

    也许。但是相对路径可以解释为“在同一个模块中”。在不同模块中存在多个同名目录的情况下,它们提供了清晰性。

  • @bk1e

    ...它经常会意外工作...

    我认为相对路径只会在非常罕见的情况下偶然起作用,即项目从一开始就被破坏并且可以很容易地修复。经历这样的名称冲突而不导致编译器错误似乎不太可能。一种常见的情况是,依赖项目的文件包含您的一个标头,其中包含您的另一个标头。与该依赖项目隔离编译时,编译您的测试套件应导致“没有此类文件或目录”错误。

  • @singlenegationelimination

    ...这不是便携式的,标准不支持它。

    ISO C 标准可能没有指定编译或运行程序的系统的所有细节。这并不意味着它们不受支持,只是标准没有过度指定 C 将运行的平台。(在常见的现代系统上如何解释""和解释之间的区别可能起源于 POSIX 标准。)<>

  • @daniel-paull

    “..”假设相对位置并且很脆弱

    脆弱如何?想必对这两个文件的搭配很敏感。因此..,仅当包含文件的作者控制其位置时,才应(并且始终)使用“”。

于 2018-09-03T06:19:56.730 回答
9

IANALL,但我认为您不应该将..'s 放在实际的 C 或 C++ 源文件中,因为这不是可移植的,并且标准不支持它。这类似于\在 Windows 上使用 's。仅当您的编译器无法使用任何其他方法时才这样做。

于 2009-02-28T03:02:51.470 回答
2

将您的源代码树视为嵌套命名空间,包含路径允许您将目录拉入此命名空间的根目录。那么问题就是为您的代码库形成一个逻辑命名空间,而不管代码在磁盘上是如何组织的。

我会避免这样的路径:

  • "include/foo/bar.h"—“包含”似乎不合逻辑且多余
  • "../foo/bar.h"—“..”假定相对位置并且很脆弱
  • "bar.h"— 除非 bar.h 在当前目录中,否则这会污染全局命名空间并要求歧义。

就个人而言,我倾向于在我的项目中添加如下路径包括路径 - "..;../..;../../..;../../../.."

这允许您对您的 s 应用一种隐藏规则,#include并允许在不破坏其他代码的情况下自由移动标题。当然,如果您不小心,这是以引入绑定到错误头文件的风险为代价的,因为非完全限定名称可能(或随着时间的推移变得)模棱两可。

我倾向于#include在公共标头中完全限定 s,因此任何使用我的代码的第三方都不需要将 s 添加"..;../..;../../..;../../../.."到他们的项目中——这只是为我的私有代码和构建系统提供便利。

于 2009-02-28T02:42:34.030 回答
2

具有相对路径的 Windows 上的另一个问题是 MAX_PATH。这将在例如为 android 交叉编译并且您的路径长度超过 260 时触发编译问题。

于 2017-10-13T15:20:41.833 回答
1

因为然后您将文件相对于项目的根目录放置,并且当您将其签入源代码控制并且另一个开发人员将其签出到其本地系统上的不同位置时,事情仍然有效。

于 2009-02-28T01:41:25.660 回答
0

为绝对(或“扩展相对”)抛出另一个论点包括路径#include "./lib/subdir/~/subsubsubsubdir/header.h"......当处理一个非常大的项目时,可能需要在链接行上有很多选项。"-I./~"在我当前的项目中,这些选项占用的字符数接近 40k,由于命令行限制而导致错误。

尽管通常我不喜欢这种包含路径风格的明显不灵活,但它可以帮助避免此类问题。例如,静态库可以通过其 API 提供"#include "lib_api.h",所有其他组织都可以与该位置相关并且对用户不可见。

于 2020-05-19T21:10:56.157 回答