C++ 为什么有头文件和 .cpp 文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/333889/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why have header files and .cpp files?
提问by paercebal
Why does C++ have header files and .cpp files?
为什么 C++ 有头文件和 .cpp 文件?
采纳答案by Joris Timmermans
Well, the main reason would be for separating the interface from the implementation. The header declares "what" a class (or whatever is being implemented) will do, while the cpp file defines "how" it will perform those features.
嗯,主要原因是将接口与实现分开。头文件声明了一个类(或正在实现的任何东西)将做什么,而 cpp 文件定义了它将“如何”执行这些功能。
This reduces dependencies so that code that uses the header doesn't necessarily need to know all the details of the implementation and any other classes/headers needed only for that. This will reduce compilation times and also the amount of recompilation needed when something in the implementation changes.
这减少了依赖性,因此使用标头的代码不一定需要知道实现的所有细节以及仅需要的任何其他类/标头。这将减少编译时间以及当实现中的某些内容发生变化时所需的重新编译量。
It's not perfect, and you would usually resort to techniques like the Pimpl Idiomto properly separate interface and implementation, but it's a good start.
它并不完美,您通常会求助于Pimpl Idiom 之类的技术来正确分离接口和实现,但这是一个好的开始。
回答by paercebal
C++ compilation
C++编译
A compilation in C++ is done in 2 major phases:
C++ 编译分两个主要阶段完成:
The first is the compilation of "source" text files into binary "object" files: The CPP file is the compiled file and is compiled without any knowledge about the other CPP files (or even libraries), unless fed to it through raw declaration or header inclusion. The CPP file is usually compiled into a .OBJ or a .O "object" file.
The second is the linking together of all the "object" files, and thus, the creation of the final binary file (either a library or an executable).
第一个是将“源”文本文件编译为二进制“目标”文件:CPP 文件是编译后的文件,编译时无需了解其他 CPP 文件(甚至库),除非通过原始声明或标题包含。CPP 文件通常被编译成 .OBJ 或 .O“对象”文件。
第二个是将所有“目标”文件链接在一起,从而创建最终的二进制文件(库或可执行文件)。
Where does the HPP fit in all this process?
HPP 在所有这些过程中处于什么位置?
A poor lonesome CPP file...
一个可怜的寂寞CPP文件...
The compilation of each CPP file is independent from all other CPP files, which means that if A.CPP needs a symbol defined in B.CPP, like:
每个 CPP 文件的编译独立于所有其他 CPP 文件,这意味着如果 A.CPP 需要在 B.CPP 中定义一个符号,例如:
// A.CPP
void doSomething()
{
doSomethingElse(); // Defined in B.CPP
}
// B.CPP
void doSomethingElse()
{
// Etc.
}
It won't compile because A.CPP has no way to know "doSomethingElse" exists... Unless there is a declaration in A.CPP, like:
它不会编译,因为 A.CPP 无法知道“doSomethingElse”的存在......除非 A.CPP 中有声明,例如:
// A.CPP
void doSomethingElse() ; // From B.CPP
void doSomething()
{
doSomethingElse() ; // Defined in B.CPP
}
Then, if you have C.CPP which uses the same symbol, you then copy/paste the declaration...
然后,如果您有使用相同符号的 C.CPP,则复制/粘贴声明...
COPY/PASTE ALERT!
复制/粘贴警报!
Yes, there is a problem. Copy/pastes are dangerous, and difficult to maintain. Which means that it would be cool if we had some way to NOT copy/paste, and still declare the symbol... How can we do it? By the include of some text file, which is commonly suffixed by .h, .hxx, .h++ or, my preferred for C++ files, .hpp:
是的,有问题。复制/粘贴很危险,而且难以维护。这意味着如果我们有办法不复制/粘贴,并且仍然声明符号会很酷......我们怎么做?通过包含一些文本文件,它通常以 .h、.hxx、.h++ 或我喜欢的 C++ 文件后缀 .hpp 为后缀:
// B.HPP (here, we decided to declare every symbol defined in B.CPP)
void doSomethingElse() ;
// A.CPP
#include "B.HPP"
void doSomething()
{
doSomethingElse() ; // Defined in B.CPP
}
// B.CPP
#include "B.HPP"
void doSomethingElse()
{
// Etc.
}
// C.CPP
#include "B.HPP"
void doSomethingAgain()
{
doSomethingElse() ; // Defined in B.CPP
}
How does include
work?
如何include
工作?
Including a file will, in essence, parse and then copy-paste its content in the CPP file.
包含文件本质上将解析然后将其内容复制粘贴到 CPP 文件中。
For example, in the following code, with the A.HPP header:
例如,在以下代码中,带有 A.HPP 标头:
// A.HPP
void someFunction();
void someOtherFunction();
... the source B.CPP:
... 来源 B.CPP:
// B.CPP
#include "A.HPP"
void doSomething()
{
// Etc.
}
... will become after inclusion:
...将在包含后变为:
// B.CPP
void someFunction();
void someOtherFunction();
void doSomething()
{
// Etc.
}
One small thing - why include B.HPP in B.CPP?
一件小事 - 为什么在 B.CPP 中包含 B.HPP?
In the current case, this is not needed, and B.HPP has the doSomethingElse
function declaration, and B.CPP has the doSomethingElse
function definition (which is, by itself a declaration). But in a more general case, where B.HPP is used for declarations (and inline code), there could be no corresponding definition (for example, enums, plain structs, etc.), so the include could be needed if B.CPP uses those declaration from B.HPP. All in all, it is "good taste" for a source to include by default its header.
在当前情况下,这是不需要的,B.HPP 有doSomethingElse
函数声明,B.CPP 有doSomethingElse
函数定义(它本身就是一个声明)。但在更一般的情况下,B.HPP 用于声明(和内联代码),可能没有相应的定义(例如,枚举、普通结构等),因此如果 B.CPP 可能需要包含使用 B.HPP 的那些声明。总而言之,默认情况下源包含其标头是“好品味”。
Conclusion
结论
The header file is thus necessary, because the C++ compiler is unable to search for symbol declarations alone, and thus, you must help it by including those declarations.
因此头文件是必要的,因为 C++ 编译器无法单独搜索符号声明,因此,您必须通过包含这些声明来帮助它。
One last word: You should put header guards around the content of your HPP files, to be sure multiple inclusions won't break anything, but all in all, I believe the main reason for existence of HPP files is explained above.
最后一句话:你应该在你的 HPP 文件的内容周围放置标题保护,以确保多个包含不会破坏任何东西,但总而言之,我相信上面解释了 HPP 文件存在的主要原因。
#ifndef B_HPP_
#define B_HPP_
// The declarations in the B.hpp file
#endif // B_HPP_
or even simpler
甚至更简单
#pragma once
// The declarations in the B.hpp file
回答by jalf
Because C, where the concept originated, is 30 years old, and back then, it was the only viable way to link together code from multiple files.
因为这个概念起源的 C 已经有 30 年的历史了,在那个时候,它是将多个文件中的代码链接在一起的唯一可行方法。
Today, it's an awful hack which totally destroys compilation time in C++, causes countless needless dependencies (because class definitions in a header file expose too much information about the implementation), and so on.
今天,这是一个可怕的黑客,它完全破坏了 C++ 中的编译时间,导致无数不必要的依赖(因为头文件中的类定义暴露了太多关于实现的信息),等等。
回答by unwind
Because in C++, the final executable code does not carry any symbol information, it's more or less pure machine code.
因为在 C++ 中,最终的可执行代码不携带任何符号信息,它或多或少是纯机器代码。
Thus, you need a way to describe the interface of a piece of code, that is separate from the code itself. This description is in the header file.
因此,您需要一种方法来描述一段代码的接口,它与代码本身是分开的。这个描述在头文件中。
回答by andref
Because C++ inherited them from C. Unfortunately.
因为 C++ 从 C 继承了它们。不幸的是。
回答by Aaron Digulla
Because the people who designed the library format didn't want to "waste" space for rarely used information like C preprocessor macros and function declarations.
因为设计库格式的人不想为很少使用的信息(如 C 预处理器宏和函数声明)“浪费”空间。
Since you need that info to tell your compiler "this function is available later when the linker is doing its job", they had to come up with a second file where this shared information could be stored.
由于您需要该信息来告诉编译器“此函数稍后在链接器执行其工作时可用”,因此他们必须想出第二个文件来存储此共享信息。
Most languages after C/C++ store this information in the output (Java bytecode, for example) or they don't use a precompiled format at all, get always distributed in source form and compile on the fly (Python, Perl).
C/C++ 之后的大多数语言都将此信息存储在输出中(例如 Java 字节码),或者它们根本不使用预编译格式,总是以源代码形式分发并即时编译(Python、Perl)。
回答by Martin v. L?wis
It's the preprocessor way of declaring interfaces. You put the interface (method declarations) into the header file, and the implementation into the cpp. Applications using your library only need to know the interface, which they can access through #include.
这是声明接口的预处理器方式。您将接口(方法声明)放入头文件,并将实现放入 cpp。使用您的库的应用程序只需要知道可以通过 #include 访问的接口。
回答by Martin v. L?wis
Often you will want to have a definition of an interface without having to ship the entire code. For example, if you have a shared library, you would ship a header file with it which defines all the functions and symbols used in the shared library. Without header files, you would need to ship the source.
通常,您需要定义一个接口,而不必发布整个代码。例如,如果你有一个共享库,你会随它一起提供一个头文件,它定义了共享库中使用的所有函数和符号。如果没有头文件,您将需要发送源代码。
Within a single project, header files are used, IMHO, for at least two purposes:
在单个项目中,使用头文件,恕我直言,至少有两个目的:
- Clarity, that is, by keeping the interfaces separate from the implementation, it is easier to read the code
- Compile time. By using only the interface where possible, instead of the full implementation, the compile time can be reduced because the compiler can simply make a reference to the interface instead of having to parse the actual code (which, idealy, would only need to be done a single time).
- 清晰,即通过将接口与实现分开,更容易阅读代码
- 编译时间。通过在可能的情况下仅使用接口而不是完整的实现,可以减少编译时间,因为编译器可以简单地引用接口而不必解析实际代码(理想情况下,只需要完成一次)。
回答by Alex v
Responding to MadKeithV's answer,
This reduces dependencies so that code that uses the header doesn't necessarily need to know all the details of the implementation and any other classes/headers needed only for that. This will reduce compilation times, and also the amount of recompilation needed when something in the implementation changes.
这减少了依赖性,因此使用标头的代码不一定需要知道实现的所有细节以及仅需要的任何其他类/标头。这将减少编译时间,以及当实现中的某些内容发生变化时所需的重新编译量。
Another reason is that a header gives a unique id to each class.
另一个原因是标题为每个类提供了唯一的 id。
So if we have something like
所以如果我们有类似的东西
class A {..};
class B : public A {...};
class C {
include A.cpp;
include B.cpp;
.....
};
We will have errors, when we try to build the project, since A is part of B, with headers we would avoid this kind of headache...
当我们尝试构建项目时,我们会遇到错误,因为 A 是 B 的一部分,使用标题我们可以避免这种头痛......