2

我正在研究一小段 C/C++ 源代码。该程序从标准输入读取输入值,用算法处理它们并将结果写入标准输出。

我只想在一个文件中实现所有这些,但我还想要算法的测试用例(不是输入/输出读取),所以我的项目中有以下文件:

  • 主文件
  • 排序文件
  • 排序测试.cpp

我立即在 sort.hpp 中实现算法,没有 sort.cpp。它相当短,没有任何依赖关系。

你会说,在某些情况下,头文件中定义的函数是可以的,即使它们是复杂的算法而不仅仅是简单的访问器/突变器?或者有什么理由我应该避免这种情况?我什么时候应该将代码从头文件移动到源文件?

4

8 回答 8

8

只要您了解权衡取舍,在头文件中包含函数就没有错。将它们放在头文件中意味着它们必须在包含头文件的任何翻译单元中进行编译(和重新编译)。(并且必须声明它们inline,否则您将收到链接器错误。)

在具有许多翻译单元的项目中,如果您经常这样做,编译时间可能会明显减慢。

另一方面,它确保函数定义在函数被调用的任何地方都是可见的——这意味着它可以被简单地内联,因此生成的程序可以运行得更快。

最后,使用函数模板,您通常没有现实的选择。定义必须在调用站点可见,实现这一点的唯一实用方法是将其放在标题中。

最后一个考虑因素是仅标头库更易于部署和使用。你不需要链接任何东西,你不必担心 ABI 或其他任何东西。您只需将标题添加到您的项目中,包括它们,然后就可以使用了。

相当多的流行库使用仅标头策略。

于 2011-04-15T13:13:28.683 回答
1

任何函数都可以放在头部,只要是inline. 内部定义的函数class { }和模板之类的东西是隐式的inline

如果生成的应用程序变得太大,则优化代码大小。在出现问题之前进行优化是一种反模式,尤其是当“按照自己的方式”进行优化有好处时,修复就像从一个文件移动到另一个文件并擦除inline.

当然,如果您想将代码作为库分发,那么在头文件、静态库或动态库二进制文件之间做出选择是影响用户的重要决定。

于 2011-04-15T13:09:24.500 回答
1

当你把函数放在头文件中时,你必须确保声明它们inline。当多个 .cpp 文件包含该头文件时,这是避免重复定义警告所必需的。通常你应该只将小函数放在头文件中,因为它将为每个包含头文件的 cpp 文件编译,这会减慢编译时间并导致代码膨胀;更大的可执行文件。

于 2011-04-15T13:13:08.880 回答
0

可以在标题中实现。这取决于你需要什么。如果您将定义分离到不同的文件中,那么编译器将创建具有外部链接的符号,如果您不希望您可以在标头本身内部定义函数。但是你会为代码段浪费一些内存。如果将此头文件包含在两个不同的文件中,则两个文件代码段都将具有此函数定义。

如果其他头文件将具有类似名称的函数,那么它将成为一个问题。然后你必须使用内联。

于 2011-04-15T13:28:28.053 回答
0

这真的是一个很挑剔的选择。但是把它放在头文件中确实意味着它将是内联代码而不是函数。如果你想要同样的功能,你可以使用 inline 关键字:

inline int max(int a, int b)
{
  return (a > b) ? a : b;
}

http://en.wikipedia.org/wiki/Inline_function

于 2011-04-15T13:09:51.707 回答
0

通常应该避免这种情况的原因(对于非内联函数)是因为多个源文件将包含您的标头,从而产生链接器错误。如果你有一个 pramga once 或类似的技巧并不重要 - 如果你有多个编译单元(例如 cpp 文件)包含相同的头文件,则会出现重复。

于 2011-04-15T13:11:49.853 回答
0

绝大多数的boost库都是只有标头的,所以我想说:是的,这是一种既定且公认的做法。只是不要忘记inline

于 2011-04-15T13:15:49.243 回答
0

如果你想内联函数,它必须在标题中,否则它不能被内联。

如果您使用库发布标头并且标头中有某种实现,那么您可以确定几年后如果您更改实现并且它的工作方式与以前完全不同,有些人代码将中断,因为 thay 将依赖他们在标题中看到的实现。是的,我知道一个人不应该这样做,但是很多人确实在标题中寻找实现和其他行为,他们可以以一种非预期的方式利用/使用来克服他们遇到的一些问题。

如果您打算使用模板,那么您别无选择,只能将其全部放在标题中。(如果您的编译器支持导出模板但我只知道 1 个,这可能不是必需的)。

于 2011-04-15T13:17:06.123 回答