Mar*_*ius 124 c++ language-design
我从来没有真正理解为什么C++需要一个单独的头文件,其功能与.cpp文件相同.它使创建类和重构它们变得非常困难,并且它为项目添加了不必要的文件.然后是必须包含头文件的问题,但必须明确检查它是否已被包含.
C++于1998年获得批准,那为什么这样设计呢?单独的头文件有哪些优点?
当我包含的所有内容都是.h文件时,编译器如何找到带有代码的.cpp文件?是否假设.cpp文件与.h文件具有相同的名称,或者它是否实际查看目录树中的所有文件?
Ste*_*sop 99
您似乎要求将定义与声明分开,尽管头文件还有其他用途.
答案是C++并不"需要"这个.如果您将所有内联标记(无论如何对于类定义中定义的成员函数都是自动的),则不需要分离.您只需在头文件中定义所有内容即可.
您可能想要分开的原因是:
如果您更普遍的问题是"为什么C++与Java不相同?",那么我不得不问,"为什么要编写C++而不是Java?" ;-p
但更严重的是,原因是C++编译器不能直接进入另一个翻译单元,并以javac可以做到的方式找出如何使用它的符号.需要头文件来向编译器声明在链接时可以获得的内容.
这#include
是一个直接的文本替代.如果在头文件中定义所有内容,预处理器最终会在项目中创建每个源文件的大量复制和粘贴,并将其提供给编译器.C++标准在1998年被批准的事实与此无关,这是因为C++的编译环境与C语言的编译环境密切相关.
转换我的评论以回答您的后续问题:
编译器如何找到带有代码的.cpp文件
它没有,至少在它编译使用头文件的代码时没有.您要链接的函数甚至不需要编写,更不用说编译器知道.cpp
它们将在哪个文件中.调用代码在编译时需要知道的所有内容都在函数声明中表示.在链接时,您将提供.o
文件列表,或静态或动态库,有效的标头是一个承诺,函数的定义将在某处.
jal*_*alf 57
有些人认为头文件是一个优点:
最终,标题系统是设计C时70年代的神器.那时,计算机的内存非常少,将整个模块保留在内存中并不是一种选择.编译器必须从顶部开始读取文件,然后线性地完成源代码.标头机制实现了这一点.编译器不必考虑其他转换单元,只需从上到下读取代码即可.
C++保留了这个系统以实现向后兼容.
今天,没有任何意义.它效率低,容易出错且过于复杂.如果这是目标,那么有更好的方法来分离界面和实现.
但是,C++ 0x的提议之一是添加一个适当的模块系统,允许将代码编译为类似于.NET或Java,编译成更大的模块,一次性完成,无需头文件.这个提议没有在C++ 0x中得到削减,但我相信它仍然在"我们以后喜欢这样做"的类别.也许在TR2或类似的.
Mic*_*tum 26
对于我(有限 - 我通常不是C开发人员)的理解,这是根植于C.记住C不知道什么类或命名空间,它只是一个长程序.此外,必须在使用它们之前声明函数.
例如,以下应该给出编译器错误:
void SomeFunction() {
SomeOtherFunction();
}
void SomeOtherFunction() {
printf("What?");
}
Run Code Online (Sandbox Code Playgroud)
错误应该是"未声明SomeOtherFunction",因为您在声明之前调用它.解决这个问题的一种方法是将SomeOtherFunction移到SomeFunction上方.另一种方法是首先声明函数签名:
void SomeOtherFunction();
void SomeFunction() {
SomeOtherFunction();
}
void SomeOtherFunction() {
printf("What?");
}
Run Code Online (Sandbox Code Playgroud)
这让编译器知道:在代码中的某处,有一个名为SomeOtherFunction的函数返回void并且不接受任何参数.因此,如果您尝试调用SomeOtherFunction的代码,请不要惊慌,而是去寻找它.
现在,假设您在两个不同的.c文件中有SomeFunction和SomeOtherFunction.然后你必须在Some.c中#include"SomeOther.c".现在,向SomeOther.c添加一些"私有"函数.由于C不知道私有函数,因此Somec中也可以使用该函数.
这是.h文件的来源:它们指定要从.c文件中"导出"的所有函数(和变量),可以在其他.c文件中访问.这样,您获得了类似公共/私人范围的东西.此外,您可以将此.h文件提供给其他人而无需共享源代码 - .h文件也可以对编译的.lib文件起作用.
因此,主要原因是为了方便,源代码保护以及在应用程序的各个部分之间进行一些解耦.
那是C.C++引入了类和私有/公共修饰符,因此当您仍然可以询问是否需要它们时,C++ AFAIK仍然需要在使用它们之前声明函数.此外,许多C++开发人员也是C devleopers,并将他们的概念和习惯接管到C++ - 为什么要改变未破坏的东西?
ere*_*der 10
第一个优点:如果您没有头文件,则必须在其他源文件中包含源文件.这将导致包含文件在包含的文件更改时再次编译.
第二个优点:它允许共享接口,而无需在不同的单元(不同的开发人员,团队,公司等)之间共享代码.
嗯,C++ 于 1998 年获得批准,但它的使用时间比这更长,而且批准主要是确定当前的使用情况,而不是强加结构。因为 C++ 是基于 C 的,而且 C 有头文件,所以 C++ 也有头文件。
头文件的主要原因是启用文件的单独编译,并最大限度地减少依赖关系。
假设我有 foo.cpp,我想使用 bar.h/bar.cpp 文件中的代码。
我可以在 foo.cpp 中 #include "bar.h",然后编程和编译 foo.cpp,即使 bar.cpp 不存在。头文件向编译器承诺 bar.h 中的类/函数将在运行时存在,并且它已经拥有它需要知道的一切。
当然,如果当我尝试链接我的程序时 bar.h 中的函数没有主体,那么它不会链接并且我会收到错误消息。
一个副作用是您可以在不泄露源代码的情况下为用户提供头文件。
另一种情况是,如果您更改了 *.cpp 文件中代码的实现,但根本不更改标头,则只需编译 *.cpp 文件而不是使用它的所有内容。当然,如果你把大量的实现放到头文件中,那么这就没那么有用了。
C++ 旨在将现代编程语言功能添加到 C 基础结构中,而不会不必要地更改与 C 语言本身无关的任何内容。
是的,在这一点上(第一个 C++ 标准发布 10 年后,它的使用量开始大幅增长 20 年后),很容易问为什么它没有合适的模块系统。显然,当今设计的任何新语言都不会像 C++ 那样工作。但这不是 C++ 的重点。
C++ 的要点是进化,是现有实践的顺利延续,只添加新功能,而不会(经常)破坏对其用户社区来说足够有效的东西。
这意味着与其他语言相比,它使某些事情变得更困难(特别是对于开始新项目的人而言),而使某些事情变得更容易(特别是对于那些维护现有代码的人而言)。
因此,与其期望 C++ 变成 C#(这毫无意义,因为我们已经有了 C#),为什么不直接选择适合工作的工具呢?我自己,努力用现代语言编写大量新功能(我碰巧使用 C#),并且我将大量现有的 C++ 保留在 C++ 中,因为重写它没有真正的价值全部。无论如何,它们集成得非常好,所以基本上是无痛的。
对头文件的需求源于编译器用于了解其他模块中的函数和/或变量的类型信息的限制.已编译的程序或库不包含编译器绑定到其他编译单元中定义的任何对象所需的类型信息.
为了弥补这一限制,C和C++允许声明,并且这些声明可以在预处理器的#include指令的帮助下包含在使用它们的模块中.
另一方面,Java或C#等语言包括在编译器输出(类文件或程序集)中绑定所需的信息.因此,不再需要维护模块的客户端包含独立声明.
绑定信息未包含在编译器输出中的原因很简单:在运行时不需要它(在编译时进行任何类型检查).这只会浪费空间.请记住,C/C++来自可执行文件或库的大小确实很重要的时间.