8

大型 C++ 项目中的一个问题可能是构建时间。在你的依赖树中有一些你需要处理的类,但通常你会避免这样做,因为每次构建都需要很长时间。您不一定要更改其公共接口,但也许您想更改其私有成员(添加缓存变量,提取私有方法,...)。您面临的问题是,在 C++ 中,甚至私有成员都在公共头文件中声明,因此您的构建系统需要重新编译所有内容。

在这种情况下你会怎么做?

我已经勾勒出两种我知道的解决方案,但它们都有自己的缺点,也许还有一个更好的我还没有想到的解决方案。

4

6 回答 6

9

粉刺模式:

在您的头文件中,仅将公共方法和私有指针(pimpl 指针或委托)声明到前向声明的实现类。

在您的源代码中,声明实现类,将公共类的每个公共方法转发给委托,并在公共类的每个构造函数中构造您的 pimpl 类的实例。

加:

  • 允许您更改类的实现,而无需重新编译所有内容。
  • 继承很好用,只是语法有点不同。

减:

  • 写很多愚蠢的方法体来做委托。
  • 调试起来有点尴尬,因为你有大量的委托要通过。
  • 基类中有一个额外的指针,如果您有很多小对象,这可能是个问题。
于 2009-06-09T23:26:13.620 回答
6

John Lakos 的《大规模 C++ 软件设计》是一本出色的书,它解决了构建大型 C++ 项目所涉及的挑战。问题和解决方案都是立足于现实的,当然,上面的问题也得到了详细的讨论。强烈推荐。

于 2009-06-10T00:08:34.127 回答
2

使用继承:

在您的标头中,将公共方法声明为纯虚拟方法和工厂。

在您的源代码中,从您的接口派生一个实现类并实现它。在工厂的实现中返回一个实现的实例。

加:

  • 允许您更改类的实现,而无需重新编译所有内容。
  • 易于实施且万无一失。

减:

  • 定义公共基类的(公共)派生实例真的很尴尬,它应该继承公共基类的(私有)实现的一些方法。
于 2009-06-09T23:27:23.523 回答
0

您可以对由另一个类 B 中的指针引用的类 A 使用前向声明。然后您可以将类的 A 头文件包含在类 B 的实现文件中,而不是其头文件中。这样,您对 A 类所做的更改不会影响包含 B 类头文件的源文件。任何想要访问 A 类成员的类都必须包含 A 类的头文件。

于 2009-06-10T00:04:36.553 回答
0

重构和使用 pimpl/handle-body 成语,使用纯虚拟接口隐藏实现细节似乎是流行的答案。在设计大型系统时,应该考虑编译时间和开发人员的生产力。但是,如果您正在使用没有单元测试覆盖率的现有大型 C++ 系统怎么办?重构通常是不可能的。

在我接触了一些常见的头文件之后,当我不想让编译器编译世界时,我通常会做一个 makefile/script 来只编译我知道需要重新编译的文件。例如,如果我正在向一个类添加一个非虚拟私有函数,那么即使它的头文件包含在一百个其他文件中,也只需要重新编译该类的 cpp 文件。在我离开这一天之前,我开始了一个干净的构建来重建世界。

于 2009-06-10T03:01:06.790 回答
-1

没有任何。

我看到使用一个的意义,但我认为以下论点在许多情况下减轻了这一点:

  1. 清晰是第一位的。如果必须考虑两次为了运行时速度而牺牲清晰度,那么为了 编译时速度而牺牲清晰度呢?
  2. 私人成员不应该经常更换。
  3. 通常,重建所有内容不需要那么长时间。
  4. 未来会出现更快的工具,因此编译速度问题将自动缓解。您的代码不会自动变得更清晰。
  5. 无论如何,您应该经常重建。
  6. 您尝试过Incredibuild吗?

当然,这最终是一个经济决定。如果“3”的权重在您的项目中很重要,并且由于某种原因“6”不能适用,那么请继续:使用这些模板您将获得比您失去的更多的收益。

于 2009-06-10T07:43:28.247 回答