头文件中的多个类vs每个类中的单个头文件

时间:2021-01-29 15:06:33

For whatever reason, our company has a coding guideline that states:

无论出于什么原因,我们公司有一个编码指南,上面写着:

Each class shall have it's own header and implementation file.

每个类都有自己的标题和实现文件。

So if we wrote a class called MyString we would need an associated MyStringh.h and MyString.cxx.

如果我们写一个叫MyString的类我们需要一个关联的MyStringh。h和MyString.cxx。

Does anyone else do this? Has anyone seen any compiling performance repercussions as a result? Does 5000 classes in 10000 files compile just as quickly as 5000 classes in 2500 files? If not, is the difference noticeable?

还有其他人这样做吗?有没有人看到编译性能的影响?10000个文件中的5000个类能在2500个文件中快速编译5000个类吗?如果没有,差别是否显而易见?

[We code C++ and use GCC 3.4.4 as our everyday compiler]

[我们编写c++代码,并使用GCC 3.4.4作为我们的日常编译器]

12 个解决方案

#1


62  

The term here is translation unit and you really want to (if possible) have one class per translation unit ie, one class implementation per .cpp file, with a corresponding .h file of the same name.

这里的术语是翻译单元,您确实希望(如果可能的话)每个翻译单元有一个类,每个.cpp文件有一个类实现,对应的.h文件具有相同的名称。

It's usually more efficient (from a compile/link) standpoint to do things this way, especially if you're doing things like incremental link and so forth. The idea being, translation units are isolated such that, when one translation unit changes, you don't have to rebuild a lot of stuff, as you would have to if you started lumping many abstractions into a single translation unit.

从编译/链接的角度来看,以这种方式进行操作通常更有效,尤其是在处理增量链接之类的事情时。其思想是,翻译单位是孤立的,当一个翻译单位发生变化时,你不需要重新构建很多东西,如果你开始将许多抽象的东西集中到一个翻译单位中,你就必须重新构建很多东西。

Also you'll find many errors/diagnostics are reported via file name ("Error in Myclass.cpp, line 22") and it helps if there's a one-to-one correspondence between files and classes. (Or I suppose you could call it a 2 to 1 correspondence).

您还可以通过文件名(“Myclass中的错误”)报告许多错误/诊断。如果文件和类之间有一对一的对应关系,那么这就很有帮助了。(或者我想你可以叫它2比1的通信)。

#2


56  

Overwhelmed by thousands lines of code?

Having one set of header/source files per class in a directory can seem overkill. And if the number of classes goes toward 100 or 1000, it can even be frightening.

在一个目录中每个类拥有一组头文件/源文件似乎有些过分了。如果类的数量增加到100或1000个,甚至会让人害怕。

But having played with sources following the philosophy "let's put together everything", the conclusion is that only the one who wrote the file has any hope to not be lost inside. Even with an IDE, it is easy to miss things because when you're playing with a source of 20,000 lines, you just close your mind for anything not exactly refering to your problem.

但是,根据“让我们把所有的东西都放在一起”的哲学,我们得出的结论是,只有撰写这份文件的人才有希望不被遗忘。即使使用IDE,也很容易忽略一些东西,因为当您使用20,000行源代码时,您只需要关闭您的思想,而不需要任何与您的问题相关的东西。

Real life example: the class hierarchy defined in those thousand lines sources closed itself into a diamond-inheritance, and some methods were overridden in child classes by methods with exactly the same code. This was easily overlooked (who wants to explore/check a 20,000 lines source code?), and when the original method was changed (bug correction), the effect was not as universal as excepted.

真实的例子:在这千行源代码中定义的类层次结构将自己封闭为一个钻石继承,一些方法在子类中被具有完全相同代码的方法覆盖。这很容易被忽略(谁想要查看/检查2万行源代码?),并且当原始方法被修改(bug修正)时,效果并不像例外那样普遍。

Dependancies becoming circular?

I had this problem with templated code, but I saw similar problems with regular C++ and C code.

我在模板代码中遇到了这个问题,但是我在常规c++和C代码中遇到了类似的问题。

Breaking down your sources into 1 header per struct/class lets you:

把你的资料分成每个结构/类的一个标题可以让你:

  • Speed up compilation because you can use symbol forward-declaration instead of including whole objects
  • 加速编译,因为您可以使用符号forward声明,而不是包含整个对象
  • Have circular dependencies between classes (§) (i.e. class A has a pointer to B, and B has a pointer to A)
  • 有圆形类之间的依赖关系(§)(即类有一个指针指向B,B有一个指针)

In source-controlled code, class dependencies could lead to regular moving of classes up and down the file, just to make the header compile. You don't want to study the evolution of such moves when comparing the same file in different versions.

在源代码控制的代码中,类依赖可能导致类在文件上下移动,只是为了编译头。在比较不同版本的相同文件时,您不希望研究此类移动的演变。

Having separate headers makes the code more modular, faster to compile, and makes it easier to study its evolution through different versions diffs

拥有单独的头文件使代码更加模块化,编译速度更快,并且更容易通过不同的版本来研究它的演变

For my template program, I had to divide my headers into two files: The .HPP file containing the template class declaration/definition, and the .INL file containing the definitions of the said class methods.

对于我的模板程序,我必须将我的头文件分为两个文件:包含模板类声明/定义的. hpp文件和包含上述类方法定义的. inl文件。

Putting all this code inside one and only one unique header would mean putting class definitions at the begining of this file, and the method definitions at the end.

将所有这些代码放在一个唯一的头中,意味着在这个文件的开头放置类定义,在结尾放置方法定义。

And then, if someone needed only a small part of the code, with the one-header-only solution, they still would have to pay for the slower compilation.

然后,如果有人只需要代码的一小部分(只有一个头的解决方案),他们仍然需要为较慢的编译付费。

(§) Note that you can have circular dependencies between classes if you know which class owns which. This is a discussion about classes having knowledge of the existence of other classes, not shared_ptr circular dependencies antipattern.

(§)注意,可以循环依赖类之间如果你知道这类拥有它。这是关于类的讨论,类知道其他类的存在,而不是shared_ptr循环依赖反模式。

One last word: Headers should be self-sufficients

One thing, though, that must be respected by a solution of multiple headers and multiple sources.

但是,有一件事必须得到多个头文件和多个源文件的解决方案的尊重。

When you include one header, no matter which header, your source must compile cleanly.

当您包含一个header时,无论哪个header,您的源代码都必须干净地编译。

Each header should be self-sufficient. You're supposed to develop code, not treasure-hunting by greping your 10,000+ source files project to find which header defines the symbol in the 1,000 lines header you need to include just because of one enum.

每个标头应该是自给自足的。您应该通过greping您的10,000多个源文件项目来开发代码,而不是寻找宝藏,以找到仅因为一个enum就需要包含的1,000行头中的哪个头定义符号。

This means that either each header defines or forward-declare all the symbols it uses, or include all the needed headers (and only the needed headers).

这意味着要么每个标头定义或向前声明它使用的所有符号,要么包含所有需要的标头(并且只包含所需的标头)。

Question about circular dependencies

underscore-d asks:

underscore-d问道:

Can you explain how using separate headers makes any difference to circular dependencies? I don't think it does. We can trivially create a circular dependency even if both classes are fully declared in the same header, simply by forward-declaring one in advance before we declare a handle to it in the other. Everything else seems to be great points, but the idea that separate headers facilitate circular dependencies seems way off

您能解释一下使用单独的头文件对循环依赖项有什么不同吗?我不这么认为。即使两个类都在同一个报头中完全声明,我们也可以简单地创建一个循环依赖关系,只需提前声明一个类,然后在另一个中声明一个句柄。其他的似乎都是很好的点,但是分离的标题可以促进循环依赖的想法似乎是错误的

underscore_d, Nov 13 at 23:20

11月13日晚上23点20分

Let's say you have 2 class templates, A and B.

假设您有两个类模板,A和B。

Let's say the definition of class A (resp. B) has a pointer to B (resp. A). Let's also way the methods of class A (resp. B) actually call methods from B (resp. A).

我们说A类的定义(resp)B)有指向B的指针(resp)。A).让我们来看看A类(resp)的方法。B)实际调用B的方法(resp)。一个)。

You have a circular dependency both in the definition of the classes, and the implementations of their methods.

在类的定义和方法的实现中都有一个循环依赖项。

If A and B were normal classes, and A and B's methods were in .CPP files, there would be no problem: You would use a forward declaration, have a header for each class definitions, then each CPP would include both HPP.

如果A和B是正常的类,而A和B的方法是在.CPP文件中,那就没有问题了:您将使用forward声明,对每个类定义都有一个标题,然后每个CPP都包含HPP。

But as you have templates, you actually have to reproduce that patterns above, but with headers only.

但是,当您有模板时,您实际上需要复制上面的模式,但是只需要使用header。

This means:

这意味着:

  1. a definition header A.def.hpp and B.def.hpp
  2. 定义头A.def.hpp和B.def.hpp
  3. an implementation header A.inl.hpp and B.inl.hpp
  4. 一个实现头A.inl。高压泵和B.inl.hpp
  5. for convenience, a "naive" header A.hpp and B.hpp
  6. 为了方便起见,一个“幼稚的”标题a。高压泵和B.hpp

Each header will have the following traits:

每个标题将具有以下特征:

  1. In A.def.hpp (resp. B.def.hpp), you have a forward declaration of class B (resp. A), which will enable you to declare a pointer/reference to that class
  2. 在A.def.hpp(分别地。B.def.hpp),您有B类的正向声明(resp)。A),它将使您能够声明到该类的指针/引用
  3. A.inl.hpp (resp. B.inl.hpp) will include both A.def.hpp and B.def.hpp, which will enable methods from A (resp. B) to use the class B (resp. A).
  4. A.inl。高压泵(分别地。B.inl.hpp)将包括A.def.hpp和B.def.hpp,这将使A (resp)中的方法成为可能。B)使用B类(resp)。一个)。
  5. A.hpp (resp. B.hpp) will directly include both A.def.hpp and A.inl.hpp (resp. B.def.hpp and B.inl.hpp)
  6. 一个。高压泵(分别地。B.hpp)将直接包括A.def.hpp和A.inl。高压泵(分别地。B.def.hpp和B.inl.hpp)
  7. Of course, all headers need to be self sufficient, and protected by header guards
  8. 当然,所有的页眉都需要自给自足,并有页眉保护

The naive user will include A.hpp and/or B.hpp, thus ignoring the whole mess.

天真的用户将包括一个。高压泵和/或B。hpp,因此忽略了整个混乱。

And having that organization means the library writer can solve the circular dependencies between A and B while keeping both classes in separate files, easy to navigate once you understand the scheme.

拥有这样的组织意味着库编写器可以解决A和B之间的循环依赖关系,同时将两个类保存在单独的文件中,一旦理解了这个方案,就可以轻松导航。

Please note that it was an edge case (two templates knowing each other). I expect most code to not need that trick.

请注意,这是一个边缘情况(两个模板相互了解)。我希望大多数代码不需要这种技巧。

#3


10  

We do that at work, its just easier to find stuff if the class and files have the same name. As for performance, you really shouldn't have 5000 classes in a single project. If you do, some refactoring might be in order.

我们在工作中这样做,如果类和文件的名称相同,就更容易找到东西。至于性能,您真的不应该在一个项目中有5000个类。如果您这样做了,那么可能需要进行一些重构。

That said, there are instances when we have multiple classes in one file. And that is when it's just a private helper class for the main class of the file.

也就是说,当我们在一个文件中有多个类时,会有一些实例。当它只是文件主类的私有助手类时。

#4


7  

+1 for separation. I just came onto a project where some classes are in files with a different name, or lumped in with another class, and it is impossible to find these in a quick and efficient manner. You can throw more resources at a build - you can't make up lost programmer time because (s)he can't find the right file to edit.

+ 1分离。我刚刚进入了一个项目,在这个项目中,有些类以不同的名称放在文件中,或者与另一个类合并在一起,以一种快速有效的方式找到这些类是不可能的。您可以在构建中添加更多的资源——您无法弥补程序员丢失的时间,因为他无法找到合适的文件进行编辑。

#5


7  

In addition to simply being "clearer", separating classes into separate files makes it easier for multiple developers not to step on each others toes. There will be less merging when it comes time to commit changes to your version control tool.

除了简单地“更清晰”外,将类分离到单独的文件中还可以使多个开发人员更容易避免互相踩到对方的脚趾。当需要提交对版本控制工具的更改时,合并会减少。

#6


5  

Most places where I have worked have folowed this practice. I've actually written coding standards for BAE (Aust.) along with the reasons why instead of just carving something in stone with no real justification.

我工作过的大多数地方都有这种做法。实际上,我已经为BAE(欧冠)编写了编码标准,并解释了为什么不只是在石头上雕刻东西而没有真正的理由。

Concerning your question about source files, it's not so much time to compile but more an issue of being able to find the relevant code snippet in the first place. Not everyone is using an IDE. And knowing that you just look for MyClass.h and MyClass.cpp really saves time compared to running "grep MyClass *.(h|cpp)" over a bunch of files and then filtering out the #include MyClass.h statements...

关于您关于源文件的问题,现在不是编译的时候,而是能够首先找到相关代码片段的问题。不是每个人都在使用IDE。知道你只是在找我的课。h和MyClass。与在一堆文件上运行“grep MyClass *.(h|cpp)”然后过滤#include MyClass相比,cpp确实节省了时间。h语句……

Mind you there are work-arounds for the impact of large numbers of source files on compile times. See Large Scale C++ Software Design by John Lakos for an interesting discussion.

注意,在编译时,有大量的源文件的影响。请参阅John Lakos的大型c++软件设计,以获得有趣的讨论。

You might also like to read Code Complete by Steve McConnell for an excellent chapter on coding guidelines. Actualy, this book is a great read that I keep coming back to regularly

您也可以阅读Steve McConnell所完成的关于编码指南的优秀章节。事实上,这本书是我经常读的一本好书

#7


3  

The best practice, as others have said, is to place each class in its own translation unit from a code maintenance and understandability perspective. However on large scale systems this is sometimes not advisable - see the section entitled "Make Those Source Files Bigger" in this article by Bruce Dawson for a discussion of the tradeoffs.

正如其他人所说的,最佳实践是从代码维护和可理解的角度将每个类放在自己的翻译单元中。然而,在大型系统中,这有时是不可取的——请参阅Bruce Dawson在本文中题为“使这些源文件更大”的章节,以讨论权衡。

#8


2  

It's common practice to do this, especially to be able to include .h in the files that need it. Of course the performance is affected but try not to think about this problem until it arises :).
It's better to start with the files separated and after that try to merge the .h's that are commonly used together to improve performance if you really need to. It all comes down to dependencies between files and this is very specific to each project.

这是一种常见的做法,尤其是能够在需要它的文件中包含.h。当然,性能会受到影响,但在问题出现之前不要考虑这个问题:)。最好从分离的文件开始,然后尝试合并通常用于提高性能的.h。所有这些都归结为文件之间的依赖关系,这对每个项目都是非常特殊的。

#9


2  

I found these guidelines particularly useful when it comes to header files : http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Header_Files

在头文件中,我发现这些指导原则特别有用:http://goog -styleguide.googlecode.com/svn/trunk/cppguide.xml#Header_Files

#10


1  

It is very helpful to have only have one class per file, but if you do your building via bulkbuild files which include all the individual C++ files, it makes for faster compilations since startup time is relatively large for many compilers.

每个文件只有一个类是非常有用的,但是如果您通过包含所有c++文件的bulkbuild文件构建您的构建,它会使编译速度更快,因为对于许多编译器来说,启动时间相对较大。

#11


1  

I'm surprised that almost everyone is in favor of having one file per class. The problem with that is that in the age of 'refactoring' one may have a hard time keeping the file and class names in synch. Everytime you change a class name, you then have to change the file name too, which means that you have to also make a change everywhere the file is included.

我很惊讶,几乎每个人都赞成每个类都有一个文件。这样做的问题是,在“重构”时代,人们可能很难将文件和类名保持同步。每次您更改一个类名称时,您都必须更改文件名,这意味着您还必须在所有文件包含的地方进行更改。

I personally group related classes into a single files and then give such a file a meaningful name that won't have to change even if a class name changes. Having fewer files also makes scrolling through a file tree easier. I use Visual Studio on Windows and Eclipse CDT on Linux, and both have shortcut keys that take you straight to a class declaration, so finding a class declaration is easy and quick.

我个人将相关的类分组到一个文件中,然后给这样的文件一个有意义的名称,即使类名发生变化,也不需要更改。文件越少,滚动文件树就越容易。我在Windows上使用Visual Studio,在Linux上使用Eclipse CDT,它们都有快捷键,可以直接指向类声明,因此查找类声明非常简单且快速。

Having said that, I think once a project is completed, or its structure has 'solidified', and name changes become rare, it may make sense to have one class per file. I wish there was a tool that could extract classes and place them in distinct .h and .cpp files. But I don't see this as essential.

话虽如此,我认为一旦一个项目完成,或者它的结构已经“固化”,并且名称更改变得罕见,那么每个文件都有一个类可能是有意义的。我希望有一个工具可以提取类并将它们放在不同的.h和.cpp文件中。但我不认为这是必要的。

The choice also depends on the type of project one works on. In my opinion the issue doesn't deserve a black and white answer since either choice has pros and cons.

选择也取决于你所从事的项目的类型。在我看来,这个问题不应该有黑白分明的答案,因为这两种选择都有利弊。

#12


0  

The same rule applies here, but it notes a few exceptions where it is allowed Like so:

这里也适用同样的规则,但它注意到在允许的情况下有几个例外:

  • Inheritance trees
  • 继承树
  • Classes that are only used within a very limited scope
  • 只在非常有限的范围内使用的类
  • Some Utilities are simply placed in a general 'utils.h'
  • 有些实用程序简单地放在通用的“utils.h”中。

#1


62  

The term here is translation unit and you really want to (if possible) have one class per translation unit ie, one class implementation per .cpp file, with a corresponding .h file of the same name.

这里的术语是翻译单元,您确实希望(如果可能的话)每个翻译单元有一个类,每个.cpp文件有一个类实现,对应的.h文件具有相同的名称。

It's usually more efficient (from a compile/link) standpoint to do things this way, especially if you're doing things like incremental link and so forth. The idea being, translation units are isolated such that, when one translation unit changes, you don't have to rebuild a lot of stuff, as you would have to if you started lumping many abstractions into a single translation unit.

从编译/链接的角度来看,以这种方式进行操作通常更有效,尤其是在处理增量链接之类的事情时。其思想是,翻译单位是孤立的,当一个翻译单位发生变化时,你不需要重新构建很多东西,如果你开始将许多抽象的东西集中到一个翻译单位中,你就必须重新构建很多东西。

Also you'll find many errors/diagnostics are reported via file name ("Error in Myclass.cpp, line 22") and it helps if there's a one-to-one correspondence between files and classes. (Or I suppose you could call it a 2 to 1 correspondence).

您还可以通过文件名(“Myclass中的错误”)报告许多错误/诊断。如果文件和类之间有一对一的对应关系,那么这就很有帮助了。(或者我想你可以叫它2比1的通信)。

#2


56  

Overwhelmed by thousands lines of code?

Having one set of header/source files per class in a directory can seem overkill. And if the number of classes goes toward 100 or 1000, it can even be frightening.

在一个目录中每个类拥有一组头文件/源文件似乎有些过分了。如果类的数量增加到100或1000个,甚至会让人害怕。

But having played with sources following the philosophy "let's put together everything", the conclusion is that only the one who wrote the file has any hope to not be lost inside. Even with an IDE, it is easy to miss things because when you're playing with a source of 20,000 lines, you just close your mind for anything not exactly refering to your problem.

但是,根据“让我们把所有的东西都放在一起”的哲学,我们得出的结论是,只有撰写这份文件的人才有希望不被遗忘。即使使用IDE,也很容易忽略一些东西,因为当您使用20,000行源代码时,您只需要关闭您的思想,而不需要任何与您的问题相关的东西。

Real life example: the class hierarchy defined in those thousand lines sources closed itself into a diamond-inheritance, and some methods were overridden in child classes by methods with exactly the same code. This was easily overlooked (who wants to explore/check a 20,000 lines source code?), and when the original method was changed (bug correction), the effect was not as universal as excepted.

真实的例子:在这千行源代码中定义的类层次结构将自己封闭为一个钻石继承,一些方法在子类中被具有完全相同代码的方法覆盖。这很容易被忽略(谁想要查看/检查2万行源代码?),并且当原始方法被修改(bug修正)时,效果并不像例外那样普遍。

Dependancies becoming circular?

I had this problem with templated code, but I saw similar problems with regular C++ and C code.

我在模板代码中遇到了这个问题,但是我在常规c++和C代码中遇到了类似的问题。

Breaking down your sources into 1 header per struct/class lets you:

把你的资料分成每个结构/类的一个标题可以让你:

  • Speed up compilation because you can use symbol forward-declaration instead of including whole objects
  • 加速编译,因为您可以使用符号forward声明,而不是包含整个对象
  • Have circular dependencies between classes (§) (i.e. class A has a pointer to B, and B has a pointer to A)
  • 有圆形类之间的依赖关系(§)(即类有一个指针指向B,B有一个指针)

In source-controlled code, class dependencies could lead to regular moving of classes up and down the file, just to make the header compile. You don't want to study the evolution of such moves when comparing the same file in different versions.

在源代码控制的代码中,类依赖可能导致类在文件上下移动,只是为了编译头。在比较不同版本的相同文件时,您不希望研究此类移动的演变。

Having separate headers makes the code more modular, faster to compile, and makes it easier to study its evolution through different versions diffs

拥有单独的头文件使代码更加模块化,编译速度更快,并且更容易通过不同的版本来研究它的演变

For my template program, I had to divide my headers into two files: The .HPP file containing the template class declaration/definition, and the .INL file containing the definitions of the said class methods.

对于我的模板程序,我必须将我的头文件分为两个文件:包含模板类声明/定义的. hpp文件和包含上述类方法定义的. inl文件。

Putting all this code inside one and only one unique header would mean putting class definitions at the begining of this file, and the method definitions at the end.

将所有这些代码放在一个唯一的头中,意味着在这个文件的开头放置类定义,在结尾放置方法定义。

And then, if someone needed only a small part of the code, with the one-header-only solution, they still would have to pay for the slower compilation.

然后,如果有人只需要代码的一小部分(只有一个头的解决方案),他们仍然需要为较慢的编译付费。

(§) Note that you can have circular dependencies between classes if you know which class owns which. This is a discussion about classes having knowledge of the existence of other classes, not shared_ptr circular dependencies antipattern.

(§)注意,可以循环依赖类之间如果你知道这类拥有它。这是关于类的讨论,类知道其他类的存在,而不是shared_ptr循环依赖反模式。

One last word: Headers should be self-sufficients

One thing, though, that must be respected by a solution of multiple headers and multiple sources.

但是,有一件事必须得到多个头文件和多个源文件的解决方案的尊重。

When you include one header, no matter which header, your source must compile cleanly.

当您包含一个header时,无论哪个header,您的源代码都必须干净地编译。

Each header should be self-sufficient. You're supposed to develop code, not treasure-hunting by greping your 10,000+ source files project to find which header defines the symbol in the 1,000 lines header you need to include just because of one enum.

每个标头应该是自给自足的。您应该通过greping您的10,000多个源文件项目来开发代码,而不是寻找宝藏,以找到仅因为一个enum就需要包含的1,000行头中的哪个头定义符号。

This means that either each header defines or forward-declare all the symbols it uses, or include all the needed headers (and only the needed headers).

这意味着要么每个标头定义或向前声明它使用的所有符号,要么包含所有需要的标头(并且只包含所需的标头)。

Question about circular dependencies

underscore-d asks:

underscore-d问道:

Can you explain how using separate headers makes any difference to circular dependencies? I don't think it does. We can trivially create a circular dependency even if both classes are fully declared in the same header, simply by forward-declaring one in advance before we declare a handle to it in the other. Everything else seems to be great points, but the idea that separate headers facilitate circular dependencies seems way off

您能解释一下使用单独的头文件对循环依赖项有什么不同吗?我不这么认为。即使两个类都在同一个报头中完全声明,我们也可以简单地创建一个循环依赖关系,只需提前声明一个类,然后在另一个中声明一个句柄。其他的似乎都是很好的点,但是分离的标题可以促进循环依赖的想法似乎是错误的

underscore_d, Nov 13 at 23:20

11月13日晚上23点20分

Let's say you have 2 class templates, A and B.

假设您有两个类模板,A和B。

Let's say the definition of class A (resp. B) has a pointer to B (resp. A). Let's also way the methods of class A (resp. B) actually call methods from B (resp. A).

我们说A类的定义(resp)B)有指向B的指针(resp)。A).让我们来看看A类(resp)的方法。B)实际调用B的方法(resp)。一个)。

You have a circular dependency both in the definition of the classes, and the implementations of their methods.

在类的定义和方法的实现中都有一个循环依赖项。

If A and B were normal classes, and A and B's methods were in .CPP files, there would be no problem: You would use a forward declaration, have a header for each class definitions, then each CPP would include both HPP.

如果A和B是正常的类,而A和B的方法是在.CPP文件中,那就没有问题了:您将使用forward声明,对每个类定义都有一个标题,然后每个CPP都包含HPP。

But as you have templates, you actually have to reproduce that patterns above, but with headers only.

但是,当您有模板时,您实际上需要复制上面的模式,但是只需要使用header。

This means:

这意味着:

  1. a definition header A.def.hpp and B.def.hpp
  2. 定义头A.def.hpp和B.def.hpp
  3. an implementation header A.inl.hpp and B.inl.hpp
  4. 一个实现头A.inl。高压泵和B.inl.hpp
  5. for convenience, a "naive" header A.hpp and B.hpp
  6. 为了方便起见,一个“幼稚的”标题a。高压泵和B.hpp

Each header will have the following traits:

每个标题将具有以下特征:

  1. In A.def.hpp (resp. B.def.hpp), you have a forward declaration of class B (resp. A), which will enable you to declare a pointer/reference to that class
  2. 在A.def.hpp(分别地。B.def.hpp),您有B类的正向声明(resp)。A),它将使您能够声明到该类的指针/引用
  3. A.inl.hpp (resp. B.inl.hpp) will include both A.def.hpp and B.def.hpp, which will enable methods from A (resp. B) to use the class B (resp. A).
  4. A.inl。高压泵(分别地。B.inl.hpp)将包括A.def.hpp和B.def.hpp,这将使A (resp)中的方法成为可能。B)使用B类(resp)。一个)。
  5. A.hpp (resp. B.hpp) will directly include both A.def.hpp and A.inl.hpp (resp. B.def.hpp and B.inl.hpp)
  6. 一个。高压泵(分别地。B.hpp)将直接包括A.def.hpp和A.inl。高压泵(分别地。B.def.hpp和B.inl.hpp)
  7. Of course, all headers need to be self sufficient, and protected by header guards
  8. 当然,所有的页眉都需要自给自足,并有页眉保护

The naive user will include A.hpp and/or B.hpp, thus ignoring the whole mess.

天真的用户将包括一个。高压泵和/或B。hpp,因此忽略了整个混乱。

And having that organization means the library writer can solve the circular dependencies between A and B while keeping both classes in separate files, easy to navigate once you understand the scheme.

拥有这样的组织意味着库编写器可以解决A和B之间的循环依赖关系,同时将两个类保存在单独的文件中,一旦理解了这个方案,就可以轻松导航。

Please note that it was an edge case (two templates knowing each other). I expect most code to not need that trick.

请注意,这是一个边缘情况(两个模板相互了解)。我希望大多数代码不需要这种技巧。

#3


10  

We do that at work, its just easier to find stuff if the class and files have the same name. As for performance, you really shouldn't have 5000 classes in a single project. If you do, some refactoring might be in order.

我们在工作中这样做,如果类和文件的名称相同,就更容易找到东西。至于性能,您真的不应该在一个项目中有5000个类。如果您这样做了,那么可能需要进行一些重构。

That said, there are instances when we have multiple classes in one file. And that is when it's just a private helper class for the main class of the file.

也就是说,当我们在一个文件中有多个类时,会有一些实例。当它只是文件主类的私有助手类时。

#4


7  

+1 for separation. I just came onto a project where some classes are in files with a different name, or lumped in with another class, and it is impossible to find these in a quick and efficient manner. You can throw more resources at a build - you can't make up lost programmer time because (s)he can't find the right file to edit.

+ 1分离。我刚刚进入了一个项目,在这个项目中,有些类以不同的名称放在文件中,或者与另一个类合并在一起,以一种快速有效的方式找到这些类是不可能的。您可以在构建中添加更多的资源——您无法弥补程序员丢失的时间,因为他无法找到合适的文件进行编辑。

#5


7  

In addition to simply being "clearer", separating classes into separate files makes it easier for multiple developers not to step on each others toes. There will be less merging when it comes time to commit changes to your version control tool.

除了简单地“更清晰”外,将类分离到单独的文件中还可以使多个开发人员更容易避免互相踩到对方的脚趾。当需要提交对版本控制工具的更改时,合并会减少。

#6


5  

Most places where I have worked have folowed this practice. I've actually written coding standards for BAE (Aust.) along with the reasons why instead of just carving something in stone with no real justification.

我工作过的大多数地方都有这种做法。实际上,我已经为BAE(欧冠)编写了编码标准,并解释了为什么不只是在石头上雕刻东西而没有真正的理由。

Concerning your question about source files, it's not so much time to compile but more an issue of being able to find the relevant code snippet in the first place. Not everyone is using an IDE. And knowing that you just look for MyClass.h and MyClass.cpp really saves time compared to running "grep MyClass *.(h|cpp)" over a bunch of files and then filtering out the #include MyClass.h statements...

关于您关于源文件的问题,现在不是编译的时候,而是能够首先找到相关代码片段的问题。不是每个人都在使用IDE。知道你只是在找我的课。h和MyClass。与在一堆文件上运行“grep MyClass *.(h|cpp)”然后过滤#include MyClass相比,cpp确实节省了时间。h语句……

Mind you there are work-arounds for the impact of large numbers of source files on compile times. See Large Scale C++ Software Design by John Lakos for an interesting discussion.

注意,在编译时,有大量的源文件的影响。请参阅John Lakos的大型c++软件设计,以获得有趣的讨论。

You might also like to read Code Complete by Steve McConnell for an excellent chapter on coding guidelines. Actualy, this book is a great read that I keep coming back to regularly

您也可以阅读Steve McConnell所完成的关于编码指南的优秀章节。事实上,这本书是我经常读的一本好书

#7


3  

The best practice, as others have said, is to place each class in its own translation unit from a code maintenance and understandability perspective. However on large scale systems this is sometimes not advisable - see the section entitled "Make Those Source Files Bigger" in this article by Bruce Dawson for a discussion of the tradeoffs.

正如其他人所说的,最佳实践是从代码维护和可理解的角度将每个类放在自己的翻译单元中。然而,在大型系统中,这有时是不可取的——请参阅Bruce Dawson在本文中题为“使这些源文件更大”的章节,以讨论权衡。

#8


2  

It's common practice to do this, especially to be able to include .h in the files that need it. Of course the performance is affected but try not to think about this problem until it arises :).
It's better to start with the files separated and after that try to merge the .h's that are commonly used together to improve performance if you really need to. It all comes down to dependencies between files and this is very specific to each project.

这是一种常见的做法,尤其是能够在需要它的文件中包含.h。当然,性能会受到影响,但在问题出现之前不要考虑这个问题:)。最好从分离的文件开始,然后尝试合并通常用于提高性能的.h。所有这些都归结为文件之间的依赖关系,这对每个项目都是非常特殊的。

#9


2  

I found these guidelines particularly useful when it comes to header files : http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Header_Files

在头文件中,我发现这些指导原则特别有用:http://goog -styleguide.googlecode.com/svn/trunk/cppguide.xml#Header_Files

#10


1  

It is very helpful to have only have one class per file, but if you do your building via bulkbuild files which include all the individual C++ files, it makes for faster compilations since startup time is relatively large for many compilers.

每个文件只有一个类是非常有用的,但是如果您通过包含所有c++文件的bulkbuild文件构建您的构建,它会使编译速度更快,因为对于许多编译器来说,启动时间相对较大。

#11


1  

I'm surprised that almost everyone is in favor of having one file per class. The problem with that is that in the age of 'refactoring' one may have a hard time keeping the file and class names in synch. Everytime you change a class name, you then have to change the file name too, which means that you have to also make a change everywhere the file is included.

我很惊讶,几乎每个人都赞成每个类都有一个文件。这样做的问题是,在“重构”时代,人们可能很难将文件和类名保持同步。每次您更改一个类名称时,您都必须更改文件名,这意味着您还必须在所有文件包含的地方进行更改。

I personally group related classes into a single files and then give such a file a meaningful name that won't have to change even if a class name changes. Having fewer files also makes scrolling through a file tree easier. I use Visual Studio on Windows and Eclipse CDT on Linux, and both have shortcut keys that take you straight to a class declaration, so finding a class declaration is easy and quick.

我个人将相关的类分组到一个文件中,然后给这样的文件一个有意义的名称,即使类名发生变化,也不需要更改。文件越少,滚动文件树就越容易。我在Windows上使用Visual Studio,在Linux上使用Eclipse CDT,它们都有快捷键,可以直接指向类声明,因此查找类声明非常简单且快速。

Having said that, I think once a project is completed, or its structure has 'solidified', and name changes become rare, it may make sense to have one class per file. I wish there was a tool that could extract classes and place them in distinct .h and .cpp files. But I don't see this as essential.

话虽如此,我认为一旦一个项目完成,或者它的结构已经“固化”,并且名称更改变得罕见,那么每个文件都有一个类可能是有意义的。我希望有一个工具可以提取类并将它们放在不同的.h和.cpp文件中。但我不认为这是必要的。

The choice also depends on the type of project one works on. In my opinion the issue doesn't deserve a black and white answer since either choice has pros and cons.

选择也取决于你所从事的项目的类型。在我看来,这个问题不应该有黑白分明的答案,因为这两种选择都有利弊。

#12


0  

The same rule applies here, but it notes a few exceptions where it is allowed Like so:

这里也适用同样的规则,但它注意到在允许的情况下有几个例外:

  • Inheritance trees
  • 继承树
  • Classes that are only used within a very limited scope
  • 只在非常有限的范围内使用的类
  • Some Utilities are simply placed in a general 'utils.h'
  • 有些实用程序简单地放在通用的“utils.h”中。