我从未真正理解为什么c++需要一个单独的头文件,其中包含与.cpp文件中相同的函数。它使得创建类和重构类变得非常困难,并且给项目添加了不必要的文件。还有一个必须包含头文件的问题,但是必须显式地检查它是否已经被包含。
c++在1998年被批准,那么为什么要这样设计呢?使用单独的头文件有什么好处?
后续问题:
当我所包含的是.h文件时,编译器如何找到其中的代码的.cpp文件?它是假定.cpp文件与.h文件具有相同的名称,还是实际上遍历目录树中的所有文件?
我从未真正理解为什么c++需要一个单独的头文件,其中包含与.cpp文件中相同的函数。它使得创建类和重构类变得非常困难,并且给项目添加了不必要的文件。还有一个必须包含头文件的问题,但是必须显式地检查它是否已经被包含。
c++在1998年被批准,那么为什么要这样设计呢?使用单独的头文件有什么好处?
后续问题:
当我所包含的是.h文件时,编译器如何找到其中的代码的.cpp文件?它是假定.cpp文件与.h文件具有相同的名称,还是实际上遍历目录树中的所有文件?
当前回答
如果你想让编译器自动找到其他文件中定义的符号,你需要强迫程序员把这些文件放在预定义的位置(就像Java包的结构决定了项目的文件夹结构)。我更喜欢头文件。此外,你还需要你所使用的库的源代码,或者一些统一的方法来把编译器需要的信息放在二进制文件中。
其他回答
以我的理解(有限的-我通常不是一个C开发人员),这是植根于C。记住,C不知道类或名称空间是什么,它只是一个很长的程序。另外,在使用函数之前必须声明它们。
例如,下面应该给出一个编译器错误:
void SomeFunction() {
SomeOtherFunction();
}
void SomeOtherFunction() {
printf("What?");
}
错误应该是“SomeOtherFunction未声明”,因为您在声明它之前调用了它。解决这个问题的一种方法是将SomeOtherFunction移到SomeFunction之上。另一种方法是先声明函数的签名:
void SomeOtherFunction();
void SomeFunction() {
SomeOtherFunction();
}
void SomeOtherFunction() {
printf("What?");
}
这让编译器知道:在代码的某个地方,有一个名为SomeOtherFunction的函数返回void,不接受任何参数。因此,如果您遇到试图调用SomeOtherFunction的代码,不要惊慌,而是去寻找它。
现在,假设在两个不同的.c文件中有SomeFunction和SomeOtherFunction。然后你必须在Some.c中#包含“SomeOther.c”。现在,向other .c中添加一些“私有”函数。由于C不知道私有函数,因此该函数也可以在Some.c中使用。
这就是.h文件的作用:它们指定你想从一个.c文件中“导出”的所有函数(和变量),这些函数可以在其他.c文件中访问。这样,您就获得了类似于Public/Private作用域的东西。此外,您可以将这个.h文件提供给其他人,而不必共享您的源代码- .h文件也适用于编译过的.lib文件。
主要原因是为了方便,为了源代码保护和应用程序各部分之间的解耦。
那是C。c++引入了类和私有/公共修饰符,所以当你仍然可以询问是否需要它们时,c++ AFAIK仍然要求在使用它们之前声明函数。此外,许多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文件,而不需要编译使用它的所有内容。当然,如果您在头文件中放置了大量的实现,那么这就变得不那么有用了。
头文件的需求源于编译器在了解其他模块中的函数和(或)变量的类型信息方面的限制。编译后的程序或库不包含编译器绑定到其他编译单元中定义的任何对象所需的类型信息。
为了弥补这一限制,C和c++允许声明,并且可以在预处理器的#include指令的帮助下将这些声明包含到使用它们的模块中。
另一方面,像Java或c#这样的语言在编译器的输出(类文件或程序集)中包含了绑定所必需的信息。因此,不再需要维护模块的客户端所包含的独立声明。
编译器输出中没有包含绑定信息的原因很简单:在运行时不需要绑定信息(任何类型检查都发生在编译时)。这只会浪费空间。请记住,C/ c++来自于一个可执行文件或库的大小相当重要的时代。
c++是这样做的,因为C是这样做的,所以真正的问题是为什么C要这样做?维基百科对此做了一些说明。
较新的编译语言(例如 Java、c#)不使用转发 声明;标识符 从源自动识别 文件和读取直接从动态 符号库。这意味着头文件 不需要文件。
c++在1998年被批准,那么为什么要这样设计呢?使用单独的头文件有什么好处?
实际上头文件在第一次检查程序时变得非常有用,检查头文件(仅使用文本编辑器)可以让您概述程序的体系结构,不像其他语言,您必须使用复杂的工具来查看类及其成员函数。