将 C++ 定义放在头文件中是一个好习惯吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/583255/
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
Is it a good practice to place C++ definitions in header files?
提问by T.E.D.
My personal style with C++ has always to put class declarations in an include file, and definitions in a .cpp file, very much like stipulated in Loki's answer to C++ Header Files, Code Separation. Admittedly, part of the reason I like this style probably has to do with all the years I spent coding Modula-2 and Ada, both of which have a similar scheme with specification files and body files.
我个人对 C++ 的风格总是将类声明放在包含文件中,并将定义放在 .cpp 文件中,非常类似于Loki 对C++ Header Files, Code Separation的回答中的规定。诚然,我喜欢这种风格的部分原因可能与我多年来花费在编写 Modula-2 和 Ada 上的时间有关,这两者都具有类似的规范文件和正文文件方案。
I have a coworker, much more knowledgeable in C++ than I, who is insisting that all C++ declarations should, where possible, include the definitions right there in the header file. He's not saying this is a valid alternate style, or even a slightly better style, but rather this is the new universally-accepted style that everyone is now using for C++.
我有一个同事,在 C++ 方面比我更了解,他坚持所有 C++ 声明都应尽可能在头文件中包含定义。他并不是说这是一种有效的替代风格,或者甚至是稍微好一点的风格,而是说这是每个人现在都在为 C++ 使用的新的普遍接受的风格。
I'm not as limber as I used to be, so I'm not really anxious to scrabble up onto this bandwagon of his until I see a few more people up there with him. So how common is this idiom really?
我不像以前那么灵活了,所以我并不急于争先恐后地赶上他的这股潮流,直到我看到更多的人和他在一起。那么这个习语到底有多普遍呢?
Just to give some structure to the answers: Is it now The Way, very common, somewhat common, uncommon, or bug-out crazy?
只是为了给答案提供一些结构:现在是The Way,非常常见,有点常见,不常见,还是错误疯狂?
回答by Evan Teran
Your coworker is wrong, the common way is and always has been to put code in .cpp files (or whatever extension you like) and declarations in headers.
你的同事错了,常见的方法是将代码放在 .cpp 文件(或任何你喜欢的扩展名)中,并在头文件中声明。
There is occasionally some merit to putting code in the header, this can allow more clever inlining by the compiler. But at the same time, it can destroy your compile times since all code has to be processed every time it is included by the compiler.
将代码放在头文件中偶尔会有一些好处,这可以让编译器更聪明地内联。但与此同时,它会破坏您的编译时间,因为每次编译器包含所有代码时都必须对其进行处理。
Finally, it is often annoying to have circular object relationships (sometimes desired) when all the code is the headers.
最后,当所有代码都是标题时,循环对象关系(有时是需要的)通常很烦人。
Bottom line, you were right, he is wrong.
归根结底,你是对的,他是错的。
EDIT:I have been thinking about your question. There is onecase where what he says is true. templates. Many newer "modern" libraries such as boost make heavy use of templates and often are "header only." However, this should only be done when dealing with templates as it is the only way to do it when dealing with them.
编辑:我一直在考虑你的问题。有一种情况,他说的是真的。模板。许多较新的“现代”库(例如 boost)大量使用模板,并且通常是“仅标头”。但是,这应该只在处理模板时进行,因为这是处理模板时的唯一方法。
EDIT:Some people would like a little more clarification, here's some thoughts on the downsides to writing "header only" code:
编辑:有些人想要多一点澄清,这里有一些关于编写“仅标题”代码的缺点的想法:
If you search around, you will see quite a lot of people trying to find a way to reduce compile times when dealing with boost. For example: How to reduce compilation times with Boost Asio, which is seeing a 14s compile of a single 1K file with boost included. 14s may not seem to be "exploding", but it is certainly a lot longer than typical and can add up quite quickly. When dealing with a large project. Header only libraries do affect compile times in a quite measurable way. We just tolerate it because boost is so useful.
如果你四处搜索,你会看到很多人在处理 boost 时试图找到一种减少编译时间的方法。例如:如何使用 Boost Asio 减少编译时间,这是一个包含 Boost 的1K 文件的 14 秒编译。14s 可能看起来不是“爆炸”,但它肯定比典型的要长得多,并且可以很快地加起来。在处理大型项目时。仅头文件库确实以相当可衡量的方式影响编译时间。我们只是容忍它,因为 boost 非常有用。
Additionally, there are many things which cannot be done in headers only (even boost has libraries you need to link to for certain parts such as threads, filesystem, etc). A Primary example is that you cannot have simple global objects in header only libs (unless you resort to the abomination that is a singleton) as you will run into multiple definition errors. NOTE:C++17's inline variables will make this particular example doable in the future.
此外,还有很多事情不能只在头文件中完成(即使 boost 也有你需要链接到某些部分的库,如线程、文件系统等)。一个主要的例子是,你不能在只有头文件的库中拥有简单的全局对象(除非你诉诸于令人憎恶的单例),因为你会遇到多个定义错误。注意:C++17 的内联变量将使这个特定示例在未来可行。
As a final point, when using boost as an example of header only code, a huge detail often gets missed.
最后一点,当使用 boost 作为仅标头代码的示例时,经常会遗漏一个巨大的细节。
Boost is library, not user level code. so it doesn't change that often. In user code, if you put everything in headers, every little change will cause you to have to recompile the entire project. That's a monumental waste of time (and is not the case for libraries that don't change from compile to compile). When you split things between header/source and better yet, use forward declarations to reduce includes, you can save hours of recompiling when added up across a day.
Boost 是库,而不是用户级代码。所以它不会经常改变。在用户代码中,如果您将所有内容都放在头文件中,那么每一个小的更改都会导致您必须重新编译整个项目。这是对时间的巨大浪费(对于从编译到编译都不会改变的库而言,情况并非如此)。当您在标头/源代码之间拆分内容时,最好使用前向声明来减少包含,当一天累加起来时,您可以节省数小时的重新编译时间。
回答by Yes - that Jake.
The day C++ coders agree on The Way, lambs will lie down with lions, Palestinians will embrace Israelis, and cats and dogs will be allowed to marry.
C++ 程序员在The Way上达成一致的那一天,羔羊将与狮子一起躺下,巴勒斯坦人将拥抱以色列人,猫和狗将被允许结婚。
The separation between .h and .cpp files is mostly arbitrary at this point, a vestige of compiler optimizations long past. To my eye, declarations belong in the header and definitions belong in the implementation file. But, that's just habit, not religion.
.h 和 .cpp 文件之间的分离在这一点上大多是任意的,这是很久以前编译器优化的痕迹。在我看来,声明属于标题,定义属于实现文件。但是,这只是习惯,而不是宗教。
回答by Laserallan
Code in headers is generally a bad idea since it forces recompilation of all files that includes the header when you change the actual code rather than the declarations. It will also slow down compilation since you'll need to parse the code in every file that includes the header.
标头中的代码通常是一个坏主意,因为当您更改实际代码而不是声明时,它会强制重新编译包含标头的所有文件。它还会减慢编译速度,因为您需要解析每个包含标头的文件中的代码。
A reason to have code in header files is that it's generally needed for the keyword inline to work properly and when using templates that's being instanced in other cpp files.
在头文件中包含代码的一个原因是,它通常需要关键字 inline 正常工作以及使用在其他 cpp 文件中实例化的模板时。
回答by JohnMcG
What might be informing you coworker is a notion that most C++ code should be templated to allow for maximum usability. And if it's templated, then everything will need to be in a header file, so that client code can see it and instantiate it. If it's good enough for Boost and the STL, it's good enough for us.
您的同事可能会认为,大多数 C++ 代码都应该被模板化以实现最大的可用性。如果它是模板化的,那么一切都需要在头文件中,以便客户端代码可以看到它并实例化它。如果它对 Boost 和 STL 来说足够好,那对我们来说就足够了。
I don't agree with this point of view, but it may be where it's coming from.
我不同意这个观点,但这可能是它的来源。
回答by XU Bin
I think your co-worker is smart and you are also correct.
我认为你的同事很聪明,你也是对的。
The useful things I found that putting everything into the headers is that:
我发现将所有内容放入标题的有用之处在于:
No need for writing & sync headers and sources.
The structure is plain and no circular dependencies force the coder to make a "better" structure.
Portable, easy to embedded to a new project.
无需编写和同步标头和来源。
结构很简单,没有循环依赖迫使编码器制作“更好”的结构。
便携,易于嵌入到新项目中。
I do agree with the compiling time problem, but I think we should notice that:
我确实同意编译时间问题,但我认为我们应该注意:
The change of source file are very likely to change the header files which leads to the whole project be recompiled again.
Compiling speed is much faster than before. And if you have a project to be built with a long time and high frequency, it may indicates that your project design has flaws. Seperate the tasks into different projects and module can avoid this problem.
源文件的改变很可能会改变头文件,从而导致整个项目再次被重新编译。
编译速度比以前快了很多。而如果你有一个项目要建设的时间长、频率高,则可能说明你的项目设计存在缺陷。将任务分成不同的项目和模块可以避免这个问题。
Lastly I just wanna support your co-worker, just in my personal view.
最后,我只是想支持你的同事,就我个人而言。
回答by Mark Ransom
Often I'll put trivial member functions into the header file, to allow them to be inlined. But to put the entire body of code there, just to be consistent with templates? That's plain nuts.
通常我会将琐碎的成员函数放入头文件中,以允许它们被内联。但是将整个代码体放在那里,只是为了与模板保持一致?这简直是疯了。
Remember: A foolish consistency is the hobgoblin of little minds.
请记住:愚蠢的一致性是小头脑的妖精。
回答by Matthieu M.
As Tuomas said, your header should be minimal. To be complete I will expand a bit.
正如 Tuomas 所说,你的标题应该是最小的。为了完整起见,我将扩展一点。
I personally use 4 types of files in my C++
projects:
我个人在我的C++
项目中使用了 4 种类型的文件:
- Public:
- Forwarding header: in case of templates etc, this file get the forwarding declarations that will appear in the header.
- Header: this file includes the forwarding header, if any, and declare everything that I wish to be public (and defines the classes...)
- Private:
- Private header: this file is a header reserved for implementation, it includes the header and declares the helper functions / structures (for Pimpl for example or predicates). Skip if unnecessary.
- Source file: it includes the private header (or header if no private header) and defines everything (non-template...)
- 民众:
- 转发标头:在模板等的情况下,此文件获取将出现在标头中的转发声明。
- 标头:此文件包含转发标头(如果有),并声明我希望公开的所有内容(并定义类...)
- 私人的:
- 私有头文件:这个文件是为实现保留的头文件,它包括头文件并声明辅助函数/结构(例如 Pimpl 或谓词)。如果不需要,请跳过。
- 源文件:它包含私有头文件(如果没有私有头文件,则为头文件)并定义所有内容(非模板...)
Furthermore, I couple this with another rule: Do not define what you can forward declare. Though of course I am reasonable there (using Pimpl everywhere is quite a hassle).
此外,我将此与另一条规则结合起来:不要定义您可以转发声明的内容。虽然我在那里当然是合理的(在任何地方使用 Pimpl 都很麻烦)。
It means that I prefer a forward declaration over an #include
directive in my headers whenever I can get away with them.
这意味着我更喜欢#include
在我的标题中使用前向声明而不是指令,只要我能逃脱它们。
Finally, I also use a visibility rule: I limit the scopes of my symbols as much as possible so that they do not pollute the outer scopes.
最后,我还使用了一个可见性规则:我尽可能地限制我的符号的作用域,这样它们就不会污染外部作用域。
Putting it altogether:
总而言之:
// example_fwd.hpp
// Here necessary to forward declare the template class,
// you don't want people to declare them in case you wish to add
// another template symbol (with a default) later on
class MyClass;
template <class T> class MyClassT;
// example.hpp
#include "project/example_fwd.hpp"
// Those can't really be skipped
#include <string>
#include <vector>
#include "project/pimpl.hpp"
// Those can be forward declared easily
#include "project/foo_fwd.hpp"
namespace project { class Bar; }
namespace project
{
class MyClass
{
public:
struct Color // Limiting scope of enum
{
enum type { Red, Orange, Green };
};
typedef Color::type Color_t;
public:
MyClass(); // because of pimpl, I need to define the constructor
private:
struct Impl;
pimpl<Impl> mImpl; // I won't describe pimpl here :p
};
template <class T> class MyClassT: public MyClass {};
} // namespace project
// example_impl.hpp (not visible to clients)
#include "project/example.hpp"
#include "project/bar.hpp"
template <class T> void check(MyClass<T> const& c) { }
// example.cpp
#include "example_impl.hpp"
// MyClass definition
The lifesaver here is that most of the times the forward header is useless: only necessary in case of typedef
or template
and so is the implementation header ;)
这里的救星是,大部分的时间向前头是无用的:只需要在的情况下,typedef
或template
等特点,是实现头;)
回答by Anonymous
To add more fun you can add .ipp
files which contain the template implementation (that is being included in .hpp
), while .hpp
contains the interface.
为了增加乐趣,您可以添加.ipp
包含模板实现(包含在 中.hpp
)的文件,同时.hpp
包含接口。
As apart from templatized code (depending on the project this can be majority or minority of files) there is normal codeand here it is better to separate the declarations and definitions. Provide also forward-declarations where needed - this may have effect on the compilation time.
除了模板化代码(取决于项目,这可以是大多数或少数文件)还有普通代码,在这里最好将声明和定义分开。在需要时还提供前向声明 - 这可能会影响编译时间。
回答by EvilTeach
Generally, when writing a new class, I will put all the code in the class, so I don't have to look in another file for it.. After everything is working, I break the body of the methods out into the cpp file, leaving the prototypes in the hpp file.
通常,在编写新类时,我会将所有代码放在类中,因此我不必在另一个文件中查找它.. 一切正常后,我将方法主体分解为 cpp 文件,将原型留在 hpp 文件中。
回答by Virne
If this new way is really The Way, we might have been running into different direction in our projects.
如果这种新方式真的是The Way,我们可能在我们的项目中遇到了不同的方向。
Because we try to avoid all unnecessary things in headers. That includes avoiding header cascade. Code in headers will propably need some other header to be included, which will need another header and so on. If we are forced to use templates, we try avoid littering headers with template stuff too much.
因为我们试图避免标题中所有不必要的东西。这包括避免标题级联。标头中的代码可能需要包含其他一些标头,这将需要另一个标头等等。如果我们被迫使用模板,我们会尽量避免使用模板内容过多地乱扔标题。
Also we use "opaque pointer"-patternwhen applicable.
此外,我们在适用时使用“不透明指针”模式。
With these practices we can do faster builds than most of our peers. And yes... changing code or class members will not cause huge rebuilds.
通过这些实践,我们可以比大多数同行更快地构建。是的...更改代码或类成员不会导致巨大的重建。