我从未真正理解为什么c++需要一个单独的头文件,其中包含与.cpp文件中相同的函数。它使得创建类和重构类变得非常困难,并且给项目添加了不必要的文件。还有一个必须包含头文件的问题,但是必须显式地检查它是否已经被包含。

c++在1998年被批准,那么为什么要这样设计呢?使用单独的头文件有什么好处?


后续问题:

当我所包含的是.h文件时,编译器如何找到其中的代码的.cpp文件?它是假定.cpp文件与.h文件具有相同的名称,还是实际上遍历目录树中的所有文件?


当前回答

头文件的需求源于编译器在了解其他模块中的函数和(或)变量的类型信息方面的限制。编译后的程序或库不包含编译器绑定到其他编译单元中定义的任何对象所需的类型信息。

为了弥补这一限制,C和c++允许声明,并且可以在预处理器的#include指令的帮助下将这些声明包含到使用它们的模块中。

另一方面,像Java或c#这样的语言在编译器的输出(类文件或程序集)中包含了绑定所必需的信息。因此,不再需要维护模块的客户端所包含的独立声明。

编译器输出中没有包含绑定信息的原因很简单:在运行时不需要绑定信息(任何类型检查都发生在编译时)。这只会浪费空间。请记住,C/ c++来自于一个可执行文件或库的大小相当重要的时代。

其他回答

第一个优点:如果没有头文件,就必须在其他源文件中包含源文件。这将导致在包含的文件更改时重新编译包含的文件。

第二个优点:它允许在不同的单元(不同的开发人员、团队、公司等)之间共享接口而无需共享代码。

c++在1998年被批准,那么为什么要这样设计呢?使用单独的头文件有什么好处?

实际上头文件在第一次检查程序时变得非常有用,检查头文件(仅使用文本编辑器)可以让您概述程序的体系结构,不像其他语言,您必须使用复杂的工具来查看类及其成员函数。

你可以在没有头文件的情况下完美地开发c++。事实上,一些大量使用模板的库并不使用头文件/代码文件范例(参见boost)。但是在C/ c++中,你不能使用没有声明的东西。一个实用的方法 处理这个问题的方法就是使用头文件。此外,您还可以在不共享代码/实现的情况下共享接口。我认为这不是C语言的创造者所能想象到的:当你使用共享头文件时,你必须使用著名的:

#ifndef MY_HEADER_SWEET_GUARDIAN
#define MY_HEADER_SWEET_GUARDIAN

// [...]
// my header
// [...]

#endif // MY_HEADER_SWEET_GUARDIAN

这并不是真正的语言特性,而是处理多重包含的一种实用方法。

所以,我认为在创建C时,前向声明的问题被低估了,现在当使用像c++这样的高级语言时,我们必须处理这类事情。

对于我们这些可怜的c++用户来说,这又是一个负担……

c++是在1998年被批准的,但它的使用时间比那要长得多,而且批准主要是规定当前的用法,而不是强加结构。由于c++是基于C语言的,而C语言有头文件,所以c++也有头文件。

使用头文件的主要原因是支持文件的单独编译,并最小化依赖关系。

假设我有foo.cpp,我想使用bar.h/bar.cpp文件中的代码。

我可以在foo.cpp中#包含“bar.h”,然后编程和编译foo.cpp,即使bar.cpp不存在。头文件向编译器承诺bar.h中的类/函数将在运行时存在,并且它已经拥有了它需要知道的一切。

当然,如果bar.h中的函数没有body当我试图链接我的程序时,它就不会链接,我就会得到一个错误。

一个副作用是,您可以在不透露源代码的情况下为用户提供头文件。

另一种情况是,如果更改了*.cpp文件中代码的实现,但根本不更改头文件,则只需要编译*.cpp文件,而不需要编译使用它的所有内容。当然,如果您在头文件中放置了大量的实现,那么这就变得不那么有用了。

如果你想让编译器自动找到其他文件中定义的符号,你需要强迫程序员把这些文件放在预定义的位置(就像Java包的结构决定了项目的文件夹结构)。我更喜欢头文件。此外,你还需要你所使用的库的源代码,或者一些统一的方法来把编译器需要的信息放在二进制文件中。