头文件实际上好吗?[关闭]


20

我发现头文件在浏览C ++源文件时很有用,因为它们给出了类中所有函数和数据成员的“摘要”。为什么这么多其他语言(例如Ruby,Python,Java等)没有这样的功能?这是C ++的详细信息派上用场的地方吗?


我不熟悉C和C ++中的工具,但是我使用过的大多数IDE /代码编辑器都具有“结构”或“概述”视图。
Michael K

1
头文件不好-它们是必需的!-另请参见以下答案:stackoverflow.com/a/1319570/158483
皮特

Answers:


31

头文件的最初目的是允许在C中进行单遍编译和模块化。通过在使用方法之前声明方法,它仅允许单遍编译。由于我们功能强大的计算机能够进行多次编译而没有任何问题,有时甚至比C ++编译器还要快,因此这个时代已经过去了。

C ++向后兼容C需要保留头文件,但在头文件上又添加了很多文件,这导致了相当麻烦的设计。FQA中的更多内容

为了实现模块化,需要头文件作为有关模块中代码的元数据。例如。什么方法(和C ++类中)在哪个库中可用。让开发人员编写此代码很明显,因为编译时间很昂贵。如今,让编译器从代码本身生成此元数据已成为问题。Java和.NET语言通常会这样做。

所以不行。头文件不好。在那时,我们仍然必须在单独的软盘上使用编译器和链接器,并且编译花费了30分钟。如今,它们只是阻碍并且是不良设计的标志。


8
我同意您编写的大部分内容,但是最后一段使事情有些简单。头文件仍然可用于定义公共接口。
本杰明·班尼尔

3
@honk这就是我提出的问题。但是我认为现代IDE(如ElYusubov所建议的)可以否定这一点。
Matt Fichman

5
您可能会补充说,C ++委员会正在开发模块,这将使C和C ++能够在没有任何头文件的情况下工作,或者使用更有效的头文件来工作。那将使这个答案更加公平。
克拉姆

2
@MattFichman:生成头文件是一回事(大多数程序员的编辑器/ IDE可以通过某种方式自动完成),但是有关公共API的一点是,它实际上需要由实际的人来定义和设计。
本杰明·班尼尔

2
@honk是的。但是没有理由在单独的文件中定义它。它可以与实现使用相同的代码。就像C#一样。
欣快的2012年

17

尽管它们作为文档形式可能对您有用,但围绕头文件的系统效率极低。

C语言的设计使每次编译过程都可以构建一个模块。每个源文件都是在编译器的单独运行中编译的。另一方面,对于引用它们的每个源文件,头文件都被注入到该编译步骤中。

这意味着,如果头文件包含在300个源文件中,则在构建程序时,头文件将被反复解析和编译300次。一遍又一遍完全相同的事物具有完全相同的结果。这是浪费大量的时间,并且是C和C ++程序花费如此长时间构建的主要原因之一。

所有现代语言都故意避免这种荒谬的低效率。取而代之的是,通常以编译语言将必需的元数据存储在生成输出中,从而使编译文件可以充当一种快速查找参考,以描述编译文件包含的内容。头文件的所有优点都是自动创建的,您无需进行任何其他工作。

交替使用解释语言,每个加载的模块都保留在内存中。引用或包括或要求某些库将读取并编译相关的源代码,该源代码将一直驻留在程序结束之前。如果您在其他地方也需要它,则因为已经加载,所以无需进行其他工作。

无论哪种情况,都可以使用该语言的工具“浏览”此步骤创建的数据。通常,IDE将具有某种类浏览器。而且,如果该语言具有REPL,它通常也可以用于生成任何已加载对象的文档摘要。


5
并非完全正确-大多数(如果不是全部)编译器会在看到标头时对其进行预编译,因此将它们包含在下一个编译单元中并不比找到预编译的代码块更糟。就像.NET代码为您编译的每个C#文件反复“构建” system.data.types一样。C / C ++程序之所以花很长时间是因为它们实际上是经过编译(和优化)的。.NET程序的所有需求都将解析为IL,运行时通过JIT进行实际的编译。也就是说,C#代码中的300个源文件也需要花费一些时间来编译。
gbjbaanb 2012年

7

目前尚不清楚,您通过浏览文件了解功能和数据成员是什么意思。但是,大多数IDE都提供工具来浏览类并查看类数据成员。

例如:Visual Studio具有Class View并且Object browser很好地提供了您所要求的功能。就像下面的屏幕截图一样。

在此处输入图片说明

在此处输入图片说明


4

头文件的另一个缺点是程序取决于其包含顺序,并且程序员必须采取额外的步骤来确保正确性。

加上C的宏系统(由C ++继承),会导致许多陷阱和混乱情况。

为了说明,如果一个标头使用宏定义了一些符号供其使用,而另一个标头以另一种方式(例如函数的名称)使用了该符号,则包含顺序将极大地影响最终结果。有很多这样的例子。


2

我一直很喜欢头文件,因为它们为实现提供了一种接口形式,以及一些额外的信息(例如类变量),它们都在一个易于查看的文件中。

我看到很多C#代码(每个类不需要2个文件)是每个类使用2个文件编写的-一个是实际的类实现,另一个是带有接口的。这种设计非常适合模拟(在某些系统中是必需的),并且有助于定义类的文档,而无需使用IDE来查看已编译的元数据。到目前为止,我会说它的良好做法。

因此,C / C ++强制在头文件中等效(多种)接口是一件好事。

我知道其他系统的支持者由于以下原因不喜欢它们:“如果必须将文件放入2个文件中,仅破解代码就更加困难”,但是我的态度是,仅破解代码并不是一种好习惯,一旦您开始编写/设计代码再想一想,那么您将理所当然地定义标题/接口。


接口和实现在不同的文件中?如果您通过设计需要它,那么在一个文件中以Java / C#等语言定义接口(或抽象类)并在其他文件中隐藏实现仍然非常普遍。这不需要旧的和棘手的头文件。
Petter Nordlander 2012年

嗯?不是我所说的-您在2个文件中定义了接口和类的实现。
gbjbaanb 2012

2
好吧,这不需要头文件,也不会使头文件变得更好。是的,具有遗留语言的语言需要它们,但是从概念上讲,它们不需要将接口/实现拆分为不同的文件(实际上,它们使情况更糟)。
Petter Nordlander 2013年

@Petter我认为您误会了,从概念上讲,它们没有什么不同-存在一个标头以提供C语言接口,尽管您是对的-您可以将2(与许多仅标头的C代码一样)组合起来,我在很多地方都遵循了最佳实践。例如,我见过的所有C#开发人员都将接口与类分开。这样做是一个好习惯,因为您可以将接口文件包含在多个其他文件中,而不会造成污染。现在,如果您认为这种拆分是最佳实践,那么在C语言中实现拆分的方法是使用头文件。
gbjbaanb 2014年

如果标头实际上对分隔接口和实现有用,则不需要PImpl之类的东西来隐藏私有组件并使最终用户不必再针对它们重新编译。相反,我们两全其美。标头伪装成一个快速消除的分离外观,同时打开了无数陷阱,如果您需要解决这些陷阱,则需要冗长的样板代码。
underscore_d

1

我实际上会说头文件不是很好,因为它们使接口和实现变得混乱。一般而言,尤其是OOP编程的目的是拥有定义的接口并隐藏实现细节,但是C ++头文件向您显示方法,继承,公共成员(接口)以及私有方法和私有成员(实施的一部分)。更不用说在某些情况下,您最终会在头文件中内联代码或构造函数,并且某些库在头文件中包含模板代码,这实际上将实现与接口混合在一起。

我相信,其初衷是使代码可以使用其他库,对象等而不必导入脚本的全部内容。您只需要编译和链接的头文件即可。这样可以节省时间和周期。在那种情况下,这是一个不错的主意,但这只是解决这些问题的一种方法。

至于浏览程序的结构,大多数IDE都提供了该功能,并且有很多工具可以启动接口,进行代码分析,反编译等,因此您可以了解隐藏的内容。

至于为什么其他语言没有实现相同的功能?好吧,因为其他语言来自其他人,而这些设计师/创作者对事物的工作方式有不同的看法。

最好的答案是坚持做您需要做的工作,并使您感到快乐。


0

在许多编程语言中,当一个程序被细分为多个编译单元时,一个单元中的代码只有在编译器具有某种了解这些事物的方法的情况下,才能使用另一单元中定义的事物。而不是要求处理一个编译单元的编译器检查定义了当前单元中使用的任何内容的每个编译单元的整个文本,最好让编译器在处理每个单元时接收要编译的单元的全文。并从其他所有人那里得到一些信息。

在C语言中,程序员负责为每个单元创建两个文件-一个文件包含仅在编译该单元时需要的信息,另一个文件包含在编译其他单元时也需要的信息。这是一个很讨厌,很笨拙的设计,但是它避免了使用语言标准来指定有关编译器应如何生成和处理任何中间文件的任何要求。许多其他语言使用不同的方法,其中编译器将从每个源文件生成一个中间文件,该中间文件描述该源文件中所有可能由外部代码访问的内容。这种方法避免了在两个源文件中具有重复信息的需要,但是它要求编译平台以C不需要的方式定义文件语义。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.