【问题标题】:Condensing Declaration and Implementation into an HPP file将声明和实现压缩到 HPP 文件中
【发布时间】:2010-10-01 16:40:16
【问题描述】:

我已经阅读了一些关于在 C++ 中保留标头的必要性/适用性/实用性的文章,但我似乎无法在任何地方找到一个充分的理由说明/何时应该或不应该完成上述操作。我知道 boost 使用 .hpp 文件向最终用户提供模板功能,而不需要关联的 .cpp 文件,并且这种想法部分源于浏览该代码。这似乎是一种交付单个文件模块的便捷方式,比如一个新的 Wt 或 Qt 小部件(仍然坚持每个 .h 的一个类约定)。

但是,假设您对他们可以访问实现没有任何问题(比如在 OSS 的上下文中),那么给某人一个带有标头声明和实现的单个 .hpp 文件是否有任何负面的技术实现。从编译器/链接器的角度来看,它是否对实例有任何负面影响?

对此的任何意见或观点将不胜感激。

【问题讨论】:

标签: c++ module header-files


【解决方案1】:

知道 boost 使用 .hpp 文件向最终用户提供模板功能,而不需要关联的 .cpp 文件

错误的动词:不是“没有需要”,而是“没有能力”。

如果 Boost 可以,他们会将他们的库分成头文件和实现文件。事实上,他们会尽可能地这样做。

干净分离的原因很简单:只有头文件的项目的编译时间会增加极大,因为每次重新编译应用程序的最小部分时都必须读取、解析和编译相关的头文件.

仅当您碰巧重新编译该特定目标文件时才需要编译实现文件。

大型 C 和/或 C++ 项目需要 小时 来编译。而这些使用清晰地分离为头文件和目标文件。如果他们只使用头文件,我敢打赌编译时间将以天而不是小时来衡量。

但对于 Boost 的许多库,事实是模板定义可能不位于与其声明不同的编译单元中,因此这是不可能的。

【讨论】:

  • 好吧,我想有某种链接器/编译器的兔子洞会阻止你干净地做到这一点。我希望有某种 ifdef 魔法来防止这些类型的为方便起见的问题。
【解决方案2】:

.hpp-only 库的主要缺点是它们不能引用预编译模块。 .hpp 中存在的所有代码以及库中的所有代码都必须添加到您的应用程序中。这会增加二进制文件的大小,并在多次使用该库的系统上产生冗余二进制文件。

【讨论】:

  • 不能用 ifdef 集解决这个问题,还是因为 include 命令的行为和复制内容的一般行为,这在功能上有所不同?
  • 不幸的是,#ifdef 无法解决此问题。这是与生俱来的。 .cpp 文件被编译为目标文件,然后可以编译为可以重用的库。 .hpp 文件中的任何代码都必须编译成您自己的应用程序二进制文件。优点是,如果您有一个体面的编译器,这允许使用内联获得更高的性能。缺点是臃肿和冗余。
  • 这听起来像是对我最初考虑的事情以外的事情有用,但不幸的是,对我一直在考虑的事情根本没有用。感谢您的信息!
【解决方案3】:

使用模板,您没有真正的选择。理论上,export 允许您将接口与实现分开,但只有一个编译器 (Comeau) 真正支持此1,并且它正在从 C++0x 中删除。

在任何情况下,尝试将非模板函数的实现放入标题中会导致一个明显的问题:单一定义规则仍然有效,因此如果您在多个翻译单元中定义相同的函数,您将有一个问题。链接器通常会给出一个错误,说明同一个符号被定义了多个。

1虽然 大部分 EDG 编译器前端真正支持它,但其他基于 EDG 的编译器,例如 Intel 的也在一定程度上支持 export ,虽然他们没有记录,所以你不能太依赖他们。

【讨论】:

  • ODR 问题但是可以通过创建函数inline 或将其放入匿名命名空间(或将其标记为static 但我想这种做法已被弃用)轻松解决。
  • @Konrad: 是的,但这些都是糟糕的修复,除非所讨论的函数真的/真的适合内联扩展。否则,您最终可能会为每个 TU 复制大型函数,从而导致无意义的代码膨胀(尽管链接器确实可能能够合并它们;许多人添加了这个以帮助保留由模板从导致膨胀)。
猜你喜欢
  • 1970-01-01
  • 2018-10-01
  • 1970-01-01
  • 2010-12-14
  • 1970-01-01
  • 2010-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多