在头文件中放置C++定义是一个好的实践吗?

261

我的个人C ++风格一直是将类声明放在包含文件中,将定义放在.cpp文件中,非常像Loki对于 C ++ Header Files,Code Separation的回答中建议的那样。不可否认,我喜欢这种风格的原因可能与我花费的所有年份编写Modula-2和Ada有关,两者都具有规范和主体文件的类似方案。

我有一个比我更了解C ++的同事,他坚称所有C ++声明都应该在可能的情况下在头文件中直接包含定义。他并不是说这是一种有效的备用风格,甚至不是稍微好一点的风格,而是这是每个人现在都在为C ++使用的新普遍接受风格。

我不像以前那么灵活,所以我真的不急着爬上他的“车轮战车” ,直到我看到还有几个人和他一起上去。那么这个习惯用语到底有多常见?

只是为了给答案一些结构:它现在是The Way™,非常普遍,有些普遍,不太普遍还是很疯狂?


2
在头文件中编写一行函数(包括getter和setter)很常见,但如果一个函数太长了,则可能会引起注意。这种情况或许适用于小类的完整定义,而该类仅被同一头文件中的另一个类使用。 - Martin Beckett
到目前为止,我一直将所有的类定义放在头文件中。只有 Pimpl 类的定义是例外,我只在头文件中声明它们。 - Johannes Schaub - litb
3
他可能认为这是正确的方式,因为Visual C++坚持要求代码以这种方式编写。当你点击一个按钮时,实现会在头文件中生成。虽然其他人已经解释了原因,但我不知道微软为什么会鼓励这种做法。 - W.K.S
7
微软更希望每个人都使用 C# 进行编程,在 C# 中没有“头文件”和“源文件”的区别,只有一个文件。我在 C++ 和 C# 领域都有很长时间的经验,实际上,C# 的方式要容易处理得多。 - Mark Lakata
1
@MarkLakata - 这确实是他指出的其中一件事。我最近没有听到他提出这个论点,但我记得他认为Java和C#是这样工作的,而当时C#是全新的,这使其成为所有语言即将遵循的趋势。 - T.E.D.
你可以使用显式模板实例化并导出显式模板实例化,将一些模板代码移动到cpp文件中。例如,如果你在不同的单元中多次使用vector<int>,你可以有一个cpp文件来显式实例化它,然后避免在其他翻译单元中重新实例化的开销。 - Mani Zandifar
17个回答

5
我认为把所有函数定义放在头文件里是绝对荒谬的。为什么?因为头文件用作类的公共接口。它是“黑盒”的外部。
当你需要查看一个类以参考如何使用它时,应该查看头文件。头文件应该列出它能做什么(注释说明每个函数的详细使用方式),并包括成员变量列表。它不应该包括每个单独函数的实现方式,因为那是一大堆不必要的信息,只会使头文件混乱不清。

4
如果这种新方式真的是“道路”,那么我们在项目中可能一直在不同的方向上奔跑。
因为我们试图避免头文件中的所有不必要的东西。这包括避免头文件级联。头文件中的代码可能需要包含其他头文件,这将需要另一个头文件,依此类推。如果我们被迫使用模板,我们会尽量避免在头文件中过多地添加模板内容。
此外,我们在适用时使用{{link1:“不透明指针”模式}}。
通过这些实践,我们可以比大多数同行更快地构建。是的……改变代码或类成员不会导致巨大的重建。

2

我将所有实现放在类定义之外。我希望将doxygen注释放在类定义之外。


1
我知道现在有点晚了,但能不能给投反对票(或支持者)的人评论一下原因呢?这对我来说似乎是一个合理的陈述。我们使用Doxygen,这个问题肯定会出现。 - T.E.D.

2
在我看来,他只有在做模板和/或元编程时才有优势。已经有很多原因限制头文件只包含声明内容。它们只是头文件而已。如果你想要包含代码,就编译成库并连接起来。

1

这不是取决于系统的复杂性和内部约定吗?

目前我正在开发一个非常复杂的神经网络模拟器,而我需要使用的接受的编码风格是:

类定义在classname.h中
类代码在classnameCode.h中
可执行代码在classname.cpp中

这将用户构建的模拟与开发人员构建的基础类分离,并在该情况下最有效。

然而,我会惊讶地看到人们在图形应用程序或任何其它旨在为用户提供代码库以外的目的的应用程序中这样做。


1
“Class code” 和 “Executable code” 之间的区别是什么? - T.E.D.
就像我之前所说的,这是一个神经模拟器:用户创建可执行的模拟,这些模拟建立在大量充当神经元等角色的类上。 因此,我们的代码只是一些不能自行执行任何操作的类,而用户则创建可执行代码来使模拟器执行各种操作。 - Ed James
通常来说,对于大多数(如果不是全部)的程序,你不能说它“实际上无法自行完成任何操作”,你是在说“主”代码放在cpp文件中,但其他内容都没有吗? - T.E.D.
在这种情况下,有点不同。 我们编写的代码基本上是一个库,用户在此基础上构建他们的模拟,这些模拟实际上是可运行的。 可以将其视为openGL->您会得到一堆函数和对象,但如果没有cpp文件来运行它们,它们就毫无用处。 - Ed James

0

模板代码应该只在头文件中。除此之外,所有定义(除了内联函数)都应该在 .cpp 文件中。这样做的最好理由是 std 库的实现也遵循同样的规则。您不会反对 std 库开发人员在这方面是正确的。


哪些标准库?据我所知,GCC的“libstdc++”似乎在“src”中几乎没有放置任何内容,而几乎所有内容都在“include”中,无论是否必须放在头文件中。因此,我认为这不是一个准确/有用的引用。无论如何,我认为stdlibs并不是用户代码的模型:它们显然是由高技能的编码人员编写的,但是要被使用,而不是阅读:它们抽象出大多数编码人员不应该考虑的高复杂性,需要难看的“_Reserved”“__names”来避免与用户的冲突,注释和间距低于我的建议等级,等等。它们在狭义上是典范。 - underscore_d

0

我认为你的同事是正确的,只要他不在头文件中编写可执行代码的过程中。 我认为,正确的平衡是遵循GNAT Ada指示的路径,其中.ads文件为其用户和子类提供了完全足够的接口定义。

顺便说一下,Ted,你是否看过这个论坛上有关Ada绑定到CLIPS库的最近问题,你几年前写的内容现在已经不再可用(相关网页现在已关闭)。即使是针对旧版本的CLIPS,这个绑定也可以成为某些人想要在Ada 2012程序中使用CLIPS推理引擎的良好起点示例。


1
哈哈,两年后,这是一个奇怪的联系方式。我会检查一下是否还有副本,但很可能没有了。当时我为了一个人工智能课程做了那个项目,以便我可以用Ada语言编写代码,但故意将该项目设置为CC0(基本上无版权),希望有人可以不害羞地拿走它并做些什么。 - T.E.D.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接