为什么 C++需要一个单独的头文件吗?

发布于 2024-08-03 00:27:12 字数 277 浏览 9 评论 0原文

我从来没有真正理解为什么 C++ 需要一个单独的头文件,其功能与 .cpp 文件中的相同。它使得创建类和重构它们变得非常困难,并且它向项目中添加了不必要的文件。然后存在的问题是必须包含头文件,但必须显式检查它是否已包含。

C++于1998年被批准,那么为什么要这样设计呢?拥有单独的头文件有什么好处?


后续问题:

当我包含的只是 .h 文件时,编译器如何找到其中包含代码的 .cpp 文件?它是否假设 .cpp 文件与 .h 文件同名,或者它实际上会查找目录树中的所有文件?

I've never really understood why C++ needs a separate header file with the same functions as in the .cpp file. It makes creating classes and refactoring them very difficult, and it adds unnecessary files to the project. And then there is the problem with having to include header files, but having to explicitly check if it has already been included.

C++ was ratified in 1998, so why is it designed this way? What advantages does having a separate header file have?


Follow up question:

How does the compiler find the .cpp file with the code in it, when all I include is the .h file? Does it assume that the .cpp file has the same name as the .h file, or does it actually look through all the files in the directory tree?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

仙女 2024-08-10 00:27:12

有些人认为头文件是一个优点:

  • 据称它可以实现/强制/允许接口和实现的分离——但通常情况并非如此。头文件充满了实现细节(例如,必须在头文件中指定类的成员变量,即使它们不是公共接口的一部分),并且函数可以而且通常是内联定义的 头中的类声明,再次破坏了这种分离。
  • 有时据说可以改进编译时间,因为每个翻译单元都可以独立处理。然而,就编译时间而言,C++ 可能是现存最慢的语言。部分原因是同一标头的多次重复包含。大量的标头被多个翻译单元包含,需要对其进行多次解析。

归根结底,头文件系统是 70 年代设计 C 时的产物。当时,计算机的内存非常少,将整个模块保留在内存中并不是一种选择。编译器必须从顶部开始读取文件,然后线性地处理源代码。标头机制可以实现这一点。编译器不必考虑其他翻译单元,它只需要从上到下读取代码即可。

C++ 保留了这个系统是为了向后兼容。

今天看来,这毫无意义。它效率低下、容易出错并且过于复杂。如果这是的目标,那么有更好的方法来分离接口和实现。

然而,C++0x 的建议之一是添加一个适当的模块系统,允许将代码与 .NET 或 Java 类似地编译成更大的模块,全部一次性完成且无需标头。这个提案没有在 C++0x 中被淘汰,但我相信它仍然属于“我们很乐意稍后再做”类别。也许在 TR2 或类似的车里。

Some people consider header files an advantage:

  • It is claimed that it enables/enforces/allows separation of interface and implementation -- but usually, this is not the case. Header files are full of implementation details (for example member variables of a class have to be specified in the header, even though they're not part of the public interface), and functions can, and often are, defined inline in the class declaration in the header, again destroying this separation.
  • It is sometimes said to improve compile-time because each translation unit can be processed independently. And yet C++ is probably the slowest language in existence when it comes to compile-times. A part of the reason is the many many repeated inclusions of the same header. A large number of headers are included by multiple translation units, requiring them to be parsed multiple times.

Ultimately, the header system is an artifact from the 70's when C was designed. Back then, computers had very little memory, and keeping the entire module in memory just wasn't an option. A compiler had to start reading the file at the top, and then proceed linearly through the source code. The header mechanism enables this. The compiler doesn't have to consider other translation units, it just has to read the code from top to bottom.

And C++ retained this system for backwards compatibility.

Today, it makes no sense. It is inefficient, error-prone and overcomplicated. There are far better ways to separate interface and implementation, if that was the goal.

However, one of the proposals for C++0x was to add a proper module system, allowing code to be compiled similar to .NET or Java, into larger modules, all in one go and without headers. This proposal didn't make the cut in C++0x, but I believe it's still in the "we'd love to do this later" category. Perhaps in a TR2 or similar.

我不是你的备胎 2024-08-10 00:27:12

您似乎在询问如何将定义与声明分开,尽管头文件还有其他用途。

答案是 C++ 并不“需要”这个。如果将所有内容标记为内联(对于类定义中定义的成员函数来说,这无论如何都是自动的),则无需进行分离。您可以在头文件中定义所有内容。

您可能想要分离的原因是:

  1. 缩短构建时间。
  2. 在没有定义源的情况下链接代码。
  3. 避免将所有内容标记为“内联”。

如果您更普遍的问题是,“为什么 C++ 与 Java 不同?”,那么我必须问,“为什么您要编写 C++ 而不是 Java?” ;-p

但更严重的是,原因是 C++ 编译器无法像 javac 那样,直接进入另一个翻译单元并弄清楚如何使用其符号。需要头文件来向编译器声明在链接时可以使用的内容。

所以#include 是直接的文本替换。如果您在头文件中定义所有内容,则预处理器最终会创建项目中每个源文件的巨大副本和粘贴,并将其输入编译器中。 C++ 标准于 1998 年获得批准的事实与此无关,事实是 C++ 的编译环境非常接近于 C 的编译环境。

转换我的评论来回答您的后续问题:

编译器如何找到包含代码的.cpp文件

它不会,至少在编译使用头文件的代码时不会。您链接的函数甚至不需要编写,更不用说编译器知道它们将位于哪个 .cpp 文件中。调用代码在编译时需要知道的所有内容在函数声明中表达。在链接时,您将提供 .o 文件或静态或动态库的列表,而头文件实际上是一个承诺,函数的定义将位于其中的某个位置。

You seem to be asking about separating definitions from declarations, although there are other uses for header files.

The answer is that C++ doesn't "need" this. If you mark everything inline (which is automatic anyway for member functions defined in a class definition), then there is no need for the separation. You can just define everything in the header files.

The reasons you might want to separate are:

  1. To improve build times.
  2. To link against code without having the source for the definitions.
  3. To avoid marking everything "inline".

If your more general question is, "why isn't C++ identical to Java?", then I have to ask, "why are you writing C++ instead of Java?" ;-p

More seriously, though, the reason is that the C++ compiler can't just reach into another translation unit and figure out how to use its symbols, in the way that javac can and does. The header file is needed to declare to the compiler what it can expect to be available at link time.

So #include is a straight textual substitution. If you define everything in header files, the preprocessor ends up creating an enormous copy and paste of every source file in your project, and feeding that into the compiler. The fact that the C++ standard was ratified in 1998 has nothing to do with this, it's the fact that the compilation environment for C++ is based so closely on that of C.

Converting my comments to answer your follow-up question:

How does the compiler find the .cpp file with the code in it

It doesn't, at least not at the time it compiles the code that used the header file. The functions you're linking against don't even need to have been written yet, never mind the compiler knowing what .cpp file they'll be in. Everything the calling code needs to know at compile time is expressed in the function declaration. At link time you will provide a list of .o files, or static or dynamic libraries, and the header in effect is a promise that the definitions of the functions will be in there somewhere.

相权↑美人 2024-08-10 00:27:12

C++ 这样做是因为 C 这样做,所以真正的问题是为什么 C 这样做? 维基百科对此做了一些介绍。

较新的编译语言(例如
Java、C#) 不使用forward
声明;标识符是
从源自动识别
文件并直接从动态读取
库符号。这意味着标题
不需要文件。

C++ does it that way because C did it that way, so the real question is why did C do it that way? Wikipedia speaks a little to this.

Newer compiled languages (such as
Java, C#) do not use forward
declarations; identifiers are
recognized automatically from source
files and read directly from dynamic
library symbols. This means header
files are not needed.

国产ˉ祖宗 2024-08-10 00:27:12

据我(有限 - 我通常不是 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 中#include“SomeOther.c”。现在,向 SomeOther.c 添加一些“私有”函数。由于 C 不知道私有函数,因此该函数在 Some.c 中也可用。

这就是 .h 文件的用武之地:它们指定要从 .c 文件“导出”的所有函数(和变量),这些函数(和变量)可以在其他 .c 文件中访问。这样,您就可以获得诸如公共/私有范围之类的东西。此外,您可以将此 .h 文件提供给其他人,而无需共享您的源代码 - .h 文件也适用于已编译的 .lib 文件。

所以主要原因实际上是为了方便、源代码保护以及应用程序各部分之间的一些解耦。

不过那是C。 C++ 引入了类和 private/public 修饰符,因此虽然您仍然可以询问是否需要它们,但 C++ AFAIK 仍然需要在使用它们之前声明函数。此外,许多 C++ 开发人员现在也是或曾经是 C 开发人员,并将他们的概念和习惯接管到了 C++ 上 - 为什么要改变没有被破坏的东西?

To my (limited - I'm not a C developer normally) understanding, this is rooted in C. Remember that C does not know what classes or namespaces are, it's just one long program. Also, functions have to be declared before you use them.

For example, the following should give a compiler error:

void SomeFunction() {
    SomeOtherFunction();
}

void SomeOtherFunction() {
    printf("What?");
}

The error should be that "SomeOtherFunction is not declared" because you call it before it's declaration. One way of fixing this is by moving SomeOtherFunction above SomeFunction. Another approach is to declare the functions signature first:

void SomeOtherFunction();

void SomeFunction() {
    SomeOtherFunction();
}

void SomeOtherFunction() {
    printf("What?");
}

This lets the compiler know: Look somewhere in the code, there is a function called SomeOtherFunction that returns void and does not take any parameters. So if you encouter code that tries to call SomeOtherFunction, do not panic and instead go looking for it.

Now, imagine you have SomeFunction and SomeOtherFunction in two different .c files. You then have to #include "SomeOther.c" in Some.c. Now, add some "private" functions to SomeOther.c. As C does not know private functions, that function would be available in Some.c as well.

This is where .h Files come in: They specify all the functions (and variables) that you want to 'Export' from a .c file that can be accessed in other .c files. That way, you gain something like a Public/Private scope. Also, you can give this .h file to other people without having to share your source code - .h files work against compiled .lib files as well.

So the main reason is really for convenience, for source code protection and to have a bit of decoupling between the parts of your application.

That was C though. C++ introduced Classes and private/public modifiers, so while you could still ask if they are needed, C++ AFAIK still requires declaration of functions before using them. Also, many C++ Developers are or were C devleopers as well and took over their concepts and habits to C++ - why change what isn't broken?

梦中楼上月下 2024-08-10 00:27:12

第一个优点:如果没有头文件,则必须在其他源文件中包含源文件。这将导致当包含文件发生更改时再次编译包含文件。

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

First advantage: If you don't have header files, you would have to include source files in other source files. This would cause the including files to be compiled again when the included file changes.

Second advantage: It allows sharing the interfaces without sharing the code between different units (different developers, teams, companies etc..)

能怎样 2024-08-10 00:27:12

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

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

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

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

The need for header files results from the limitations that the compiler has for knowing about the type information for functions and or variables in other modules. The compiled program or library does not include the type information required by the compiler to bind to any objects defined in other compilation units.

In order to compensate for this limitation, C and C++ allow for declarations and these declarations can be included into modules that use them with the help of the preprocessor's #include directive.

Languages like Java or C# on the other hand include the information necessary for binding in the compiler's output (class-file or assembly). Hence, there is no longer a need for maintaining standalone declarations to be included by clients of a module.

The reason for the binding information not being included in the compiler output is simple: it is not needed at runtime (any type checking occurs at compile time). It would just waste space. Remember that C/C++ come from a time where the size of an executable or library did matter quite a bit.

南街女流氓 2024-08-10 00:27:12

嗯,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 文件,而不是使用它的所有内容。当然,如果您将大量实现放入头文件中,那么这就没那么有用了。

Well, C++ was ratified in 1998, but it had been in use for a lot longer than that, and the ratification was primarily setting down current usage rather than imposing structure. And since C++ was based on C, and C has header files, C++ has them too.

The main reason for header files is to enable separate compilation of files, and minimize dependencies.

Say I have foo.cpp, and I want to use code from the bar.h/bar.cpp files.

I can #include "bar.h" in foo.cpp, and then program and compile foo.cpp even if bar.cpp doesn't exist. The header file acts as a promise to the compiler that the classes/functions in bar.h will exist at run-time, and it has everything it needs to know already.

Of course, if the functions in bar.h don't have bodies when I try to link my program, then it won't link and I'll get an error.

A side-effect is that you can give users a header file without revealing your source code.

Another is that if you change the implementation of your code in the *.cpp file, but do not change the header at all, you only need to compile the *.cpp file instead of everything that uses it. Of course, if you put a lot of implementation into the header file, then this becomes less useful.

绝對不後悔。 2024-08-10 00:27:12

C++ 旨在将现代编程语言功能添加到 C 基础结构中,而不会不必要地更改与 C 语言本身无关的任何内容。

是的,在这一点上(第一个 C++ 标准发布 10 年后,它的使用量开始大幅增长 20 年后),很容易问为什么它没有合适的模块系统。显然,当今设计的任何新语言都不会像 C++ 那样工作。但这不是 C++ 的重点。

C++ 的要点是进化,是现有实践的顺利延续,只添加新功能,而不会(经常)破坏对其用户社区来说足够有效的东西。

这意味着与其他语言相比,它使某些事情变得更困难(特别是对于开始新项目的人而言),而使某些事情变得更容易(特别是对于那些维护现有代码的人而言)。

因此,与其期望 C++ 变成 C#(这毫无意义,因为我们已经有了 C#),为什么不直接选择适合工作的工具呢?我自己,努力用现代语言编写大量新功能(我碰巧使用 C#),并且我将大量现有的 C++ 保留在 C++ 中,因为重写它没有真正的价值全部。无论如何,它们集成得非常好,所以基本上是无痛的。

C++ was designed to add modern programming language features to the C infrastructure, without unnecessarily changing anything about C that wasn't specifically about the language itself.

Yes, at this point (10 years after the first C++ standard and 20 years after it began seriously growing in usage) it is easy to ask why doesn't it have a proper module system. Obviously any new language being designed today would not work like C++. But that isn't the point of C++.

The point of C++ is to be evolutionary, a smooth continuation of existing practise, only adding new capabilities without (too often) breaking things that work adequately for its user community.

This means that it makes some things harder (especially for people starting a new project), and some things easier (especially for those maintaining existing code) than other languages would do.

So rather than expecting C++ to turn into C# (which would be pointless as we already have C#), why not just pick the right tool for the job? Myself, I endeavour to write significant chunks of new functionality in a modern language (I happen to use C#), and I have a large amount of existing C++ that I am keeping in C++ because there would be no real value in re-writing it all. They integrate very nicely anyway, so it's largely painless.

坏尐絯℡ 2024-08-10 00:27:12

它不需要具有与 main 中相同功能的单独头文件。仅当您使用多个代码文件开发应用程序并且使用先前未声明的函数时才需要它。

这确实是一个范围问题。

It doesn't need a separate header file with the same functions as in main. It only needs it if you develop an application using multiple code files and if you use a function that was not previously declared.

It's really a scope problem.

向日葵 2024-08-10 00:27:12

C++在1998年就被批准了,为什么要这样设计呢?拥有单独的头文件有什么好处?

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

C++ was ratified in 1998, so why is it designed this way? What advantages does having a separate header file have?

Actually header files become very useful when examining programs for the first time, checking out header files(using only a text editor) gives you an overview of the architecture of the program, unlike other languages where you have to use sophisticated tools to view classes and their member functions.

画▽骨i 2024-08-10 00:27:12

如果你希望编译器自动找出其他文件中定义的符号,你需要强制程序员将这些文件放在预定义的位置(就像Java包结构决定项目的文件夹结构一样)。我更喜欢头文件。此外,您还需要使用的库源或某种统一的方式将编译器所需的信息放入二进制文件中。

If you want the compiler to find out symbols defined in other files automatically, you need to force programmer to put those files in predefined locations (like Java packages structure determines folders structure of the project). I prefer header files. Also you would need either sources of libraries you use or some uniform way to put information needed by compiler in binaries.

谎言 2024-08-10 00:27:12

我认为头文件背后的真正(历史)原因是让编译器开发人员更容易......但是,头文件确实具有优势。
查看这篇上一篇文章了解更多讨论...

I think the real (historical) reason behind header files was making like easier for compiler developers... but then, header files do give advantages.
Check this previous post for more discussions...

悟红尘 2024-08-10 00:27:12

好了,不用头文件就可以完美开发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++ 用户的另一个负担......

Well, you can perfectly develop C++ without header files. In fact some libraries that intensively use templates does not use the header/code files paradigm (see boost). But In C/C++ you can not use something that is not declared. One practical way to
deal with that is to use header files. Plus, you gain the advantage of sharing interface whithout sharing code/implementation. And I think it was not envisionned by the C creators : When you use shared header files you have to use the famous :

#ifndef MY_HEADER_SWEET_GUARDIAN
#define MY_HEADER_SWEET_GUARDIAN

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

#endif // MY_HEADER_SWEET_GUARDIAN

that is not really a language feature but a practical way to deal with multiple inclusion.

So, I think that when C was created, the problems with forward declaration was underestimated and now when using a high level language like C++ we have to deal with this sort of things.

Another burden for us poor C++ users ...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文