56

对于必须处理分布在多个源文件和头文件中的大量相互依赖的类的人,您建议的 C++ 编码和文件组织指南是什么?

我的项目中有这种情况,解决跨多个头文件的类定义相关错误变得相当头疼。

4

6 回答 6

78

一些一般准则:

  • 将您的接口与实现配对。如果你有foo.cxx,最好在里面声明所有定义的东西foo.h
  • 确保每个头文件#includes 所有其他必要的头文件或独立编译所需的前向声明。
  • 抵制创建“一切”标题的诱惑。他们总是在路上遇到麻烦。
  • 将一组相关(且相互依赖)的功能放入单个文件中。Java 和其他环境鼓励每个文件一个类。使用 C++,您通常需要每个文件一组类。这取决于代码的结构。
  • #include尽可能优先使用前向声明而不是s。这允许您打破循环头依赖关系。本质上,对于跨单独文件的循环依赖关系,您需要一个看起来像这样的文件依赖关系图:
    • A.cxx要求A.hB.h
    • B.cxx要求A.hB.h
    • A.h需要B.h
    • B.h是独立的(并且前向声明定义在 中的类A.h

如果您的代码打算成为其他开发人员使用的库,则需要采取一些额外的步骤:

  • 如有必要,请使用“私有标头”的概念。也就是说,多个源文件需要的头文件,但公共接口从不需要。这可能是一个具有通用内联函数、宏或内部常量的文件。
  • 在文件系统级别将您的公共接口与您的私有实现分开。我倾向于在我的 C 或 C++ 项目中使用include/和子目录,其中有我所有的公共头文件,并且有我所有的源代码。和私有标头。src/include/src/

我建议找一本 John Lakos 的书Large-Scale C++ Software Design。这是一本相当厚重的书,但如果你只是浏览一下他关于物理架构的一些讨论,你会学到很多东西。

于 2008-12-06T08:35:56.420 回答
10

查看NASA 戈达德太空飞行中心的 C 和 C++ 编码标准。我在 C 标准中特别指出并在我自己的代码中采用的一条规则是强制执行头文件的“独立”性质的规则。在头文件 xxx.h 的实现文件 xxx.cpp 中,确保 xxx.h 是包含的第一个头文件。如果头文件在任何时候都不是自包含的,那么编译将失败。这是一个非常简单而有效的规则。

唯一失败的情况是,如果您在机器之间进行移植,并且 xxx.h 标头包括,例如<pqr.h>,但<pqr.h>需要<abc.h>原始平台上的标头提供的设施(因此<pqr.h>包括<abc.h>),但这些设施不是由<abc.h>其他平台提供(它们在def.h,但<pqr.h>不包括<def.h>)。这不是规则的错误,如果您遵守规则,问题更容易诊断和解决。

于 2008-12-06T11:11:25.403 回答
6

检查Google 样式指南中的头文件部分

于 2008-12-06T08:34:49.527 回答
5

汤姆的回答非常好!

我唯一要添加的是在头文件中永远不要有“使用声明”。它们应该只允许在实现文件中使用,例如foo.cpp.

优秀的书“Accelerated C++”(亚马逊链接- 为脚本儿童链接纳粹清理过)很好地描述了这一点的逻辑

于 2008-12-06T13:50:42.557 回答
3

除了这里的其他一点之外,还有一点:

不要在包含文件中包含任何私有定义。例如,仅在 xxx.cpp 中使用的任何定义都应该在 xxx.cpp 中,而不是 xxx.h 中。

看起来很明显,但我经常看到它。

于 2008-12-07T21:58:16.440 回答
3

我想添加一个经常被放弃的非常好的实践(在 C 和 C++ 中):

foo.c

#include "foo.h" // always the first directive

任何其他需要的标题都应该跟在后面,然后是代码。关键是,无论如何,您几乎总是需要该编译单元的标头,并且将其作为第一个指令包含在内可保证标头保持自给自足(如果不是,则会出现错误)。对于公共标头尤其如此。

如果在任何时候您需要在此标头包含之前放置一些内容(当然注释除外),那么您很可能做错了什么。除非你真的知道你在做什么......这会导致另一个更重要的规则 => 评论你的黑客!

于 2013-06-30T07:04:52.260 回答