优点与优点 将所有代码放入 C++ 头文件中的缺点?

发布于 2024-07-06 17:10:06 字数 608 浏览 5 评论 0原文

您可以构造一个 C++ 程序,以便(几乎)所有代码都驻留在头文件中。 它本质上看起来像一个 C# 或 Java 程序。 但是,您确实需要至少一个 .cpp 文件来在编译时引入所有头文件。 现在我知道有些人绝对会讨厌这个想法。 但我还没有发现这样做有任何令人信服的缺点。 我可以列出一些优点:

[1] 编译时间更快。 所有头文件只解析一次,因为只有一个 .cpp 文件。 另外,一个头文件不能包含多次,否则您将遇到构建中断。 使用替代方法时,还有其他方法可以实现更快的编译,但这非常简单。

[2] 它通过使它们绝对清晰来避免循环依赖。 如果ClassA.h中的ClassAClassB.h中的ClassB有循环依赖,我必须放置一个前向参考和 它很突出。 (请注意,这与 C# 和 Java 不同,编译器会自动解决循环依赖关系。在我看来,这会鼓励不良的编码实践)。 同样,如果您的代码位于 .cpp 文件中,则可以避免循环依赖,但在实际项目中,.cpp 文件往往会包含随机标头,直到您可以'搞不清谁依赖谁。

你的想法?

You can structure a C++ program so that (almost) all the code resides in Header files. It essentially looks like a C# or Java program. However, you do need at least one .cpp file to pull in all the header files when compiling. Now I know some people would absolutely detest this idea. But I haven't found any convincing downsides of doing this. I can list some advantages:

[1] Faster compile times. All header files only get parsed once, because there is only one .cpp file. Also, one header file cannot be included more than once, otherwise you will get a build break. There are other ways of achieving faster compiles when using the alternate approach, but this is so simple.

[2] It avoids circular dependencies, by making them absolutely clear. If ClassA in ClassA.h has a circular dependency on ClassB in ClassB.h, I have to put a forward reference & it sticks out. (Note that this is unlike C# & Java where the compiler automatically resolves circular dependencies. This encourages bad coding practices IMO). Again, you can avoid circular dependencies if your code was in .cpp files, but in a real-world project, .cpp files tend to include random headers until you can't figure out who depends on whom.

Your thoughts?

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

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

发布评论

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

评论(18

晨曦慕雪 2024-07-13 17:10:06

原因 [1] 更快的编译时间

不在我的项目中:源文件 (CPP) 仅包含它们需要的标头 (HPP)。 因此,当我因为一个微小的更改而只需要重新编译一个 CPP 时,未重新编译的文件数量是原来的十倍。

也许你应该用更逻辑的源代码/标题来分解你的项目:A类实现的修改不需要重新编译B、C、D、E等的实现。

原因[2]它避免了循环依赖

循环依赖在代码中?

抱歉,但我还没有将此类问题视为真正的问题:假设 A 依赖于 B,而 B 依赖于 A:

struct A
{
   B * b ;
   void doSomethingWithB() ;
} ;

struct B
{
   A * a ;
   void doSomethingWithA() ;
} ;

void A::doSomethingWithB() { /* etc. */ }
void B::doSomethingWithA() { /* etc. */ }

解决该问题的一个好方法是将这个源分解为至少一个源/每个类的标头(与 Java 方式类似,但每个类有一个源和一个标头):

// A.hpp

struct B ;

struct A
{
   B * b ;
   void doSomethingWithB() ;
} ;

.

// B.hpp

struct A ;

struct B
{
   A * a ;
   void doSomethingWithA() ;
} ;

// A.cpp
#include "A.hpp"
#include "B.hpp"

void A::doSomethingWithB() { /* etc. */ }

// B.cpp
#include "B.hpp"
#include "A.hpp"

void B::doSomethingWithA() { /* etc. */ }

因此,不存在依赖性问题,并且编译时间仍然很快。

我错过了什么?

当从事“现实世界”项目时

在现实世界的项目中,cpp 文件往往包含随机标头,直到您无法弄清楚谁依赖于谁

。当然。 但是,如果您有时间重新组织这些文件来构建您的“one CPP”解决方案,那么您就有时间清理这些标头。 我的标头规则是:

  • 分解标头,使它们尽可能模块化
  • 永远不要包含不需要的标头
  • 向前声明它
  • 如果您需要一个符号,只有在上述失败时才

,包括标头无论如何,所有标头都必须是 self -sufficient,这意味着:

  • 一个标头包括所有需要的标头(并且仅需要标头 - 参见上文)
  • 一个包含一个标头的空 CPP 文件必须编译而无需包含其他任何内容

这将消除排序问题和循环依赖。

编译时间是一个问题吗? 那么...

如果编译时间真的是一个问题,我会考虑:

结论

您所做的并不是将所有内容都放入标头中。

您基本上将所有文件包含到一个且唯一的最终源中。

也许您在完整项目编译方面获胜。

但是当编译一个小的改变时,你总是会失败。

在编码时,我知道我经常编译一些小的更改(如果只是为了让编译器验证我的代码),然后最后一次进行完整的项目更改。

如果我的项目按照您的方式组织,我会损失很多时间。

Reason [1] Faster compile times

Not in my projects: source files (CPP) only include the headers (HPP) they need. So when I need to recompile only one CPP because of a tiny change, I have ten times the same number of files that are not recompiled.

Perhaps you should break down your project in more logical sources/headers: A modification in class A's implementation should NOT need the recompilation of implementations of class B, C, D, E, etc..

Reason[2] It avoids circular dependencies

Circular dependencies in code?

Sorry, but I have yet to have this kind of problem being a real problem: Let's say A depends on B, and B depends on A:

struct A
{
   B * b ;
   void doSomethingWithB() ;
} ;

struct B
{
   A * a ;
   void doSomethingWithA() ;
} ;

void A::doSomethingWithB() { /* etc. */ }
void B::doSomethingWithA() { /* etc. */ }

A good way to resolve the problem would be to break down this source into at least one source/header per class (in a way similar to the Java way, but with one source and one header per class):

// A.hpp

struct B ;

struct A
{
   B * b ;
   void doSomethingWithB() ;
} ;

.

// B.hpp

struct A ;

struct B
{
   A * a ;
   void doSomethingWithA() ;
} ;

.

// A.cpp
#include "A.hpp"
#include "B.hpp"

void A::doSomethingWithB() { /* etc. */ }

.

// B.cpp
#include "B.hpp"
#include "A.hpp"

void B::doSomethingWithA() { /* etc. */ }

Thus, no dependency problem, and still fast compile times.

Did I miss something?

When working on "real-world" projects

in a real-world project, cpp files tend to include random headers until you can't figure out who depends on whom

Of course. But then if you have time to reorganize those files to build your "one CPP" solution, then you have time to clean those headers. My rules for headers are:

  • break down header to make them as modular as possible
  • Never include headers you don't need
  • If you need a symbol, forward-declare it
  • only if the above failed, include the header

Anyway, all headers must be self-sufficient, which means:

  • An header include all needed headers (and only needed headers - see above)
  • an empty CPP file including one header must compile without needing to include anything else

This will remove ordering problems and circular dependencies.

Is compile times an issue? Then...

Should compile time be really an issue, I would consider either:

Conclusion

What you are doing is not putting everything in headers.

You are basically including all your files into one and only one final source.

Perhaps you are winning in terms of full-project compilation.

But when compiling for one small change, you'll always lose.

When coding, I know I compile often small changes (if only to have the compiler validate my code), and then one final time, do a full project change.

I would lose a lot of time if my project was organized your way.

很酷又爱笑 2024-07-13 17:10:06

我不同意第 1 点。

是的,只有一个 .cpp,并且从头开始的构建时间更快。 但是,您很少从头开始构建。 您进行一些小的更改,并且每次都需要重新编译整个项目。

我更喜欢用另一种方式来做:

  • 在 .h 文件中保留共享声明
  • 保留仅在 .cpp 文件中的一个位置使用的类的定义

因此,我的一些 .cpp 文件开始看起来像 Java 或 C# 代码;)

但是, “将内容保留在 .h 中” 方法在设计系统时很好,因为您提出了第 2 点。 我通常在构建类层次结构时这样做,然后当代码体系结构变得稳定时,我将代码移动到 .cpp 文件。

I disagree with point 1.

Yes, there is only one .cpp and the built time from scratch is faster. But, you rarely build from scratch. You make small changes, and it would need to recompile the whole project each time.

I prefer doing it the other way around:

  • keep shared declarations in .h files
  • keep definition for classes that are only used in one place in .cpp files

So, some of my .cpp files start looking like Java or C# code ;)

But, 'keeping stuff in .h' approach is good while designing the system, because of point 2. you made. I usually do that while I'm building the class hierarchy and later when code architecture becomes stable, I move code to .cpp files.

伏妖词 2024-07-13 17:10:06

你说你的解决方案有效是对的。 它甚至可能对您当前的项目和开发环境没有缺点。

但是...

正如其他人所说,将所有代码放入头文件中会在每次更改一行代码时强制进行完整编译。 这可能还不是问题,但您的项目可能会变得足够大,以至于编译时间将成为一个问题。

另一个问题是共享代码时。 虽然您可能还没有直接关心,但对代码的潜在用户隐藏尽可能多的代码非常重要。 通过将代码放入头文件中,任何使用您的代码的程序员都必须查看整个代码,而只对如何使用它感兴趣。 将代码放入 cpp 文件中只允许提供二进制组件(静态或动态库)及其接口作为头文件,这在某些环境中可能更简单。

如果您希望能够将当前代码转换为动态库,这就是一个问题。 由于您没有与实际代码分离的正确接口声明,因此您将无法将编译后的动态库及其使用接口作为可读头文件提供。

您可能还没有这些问题,这就是为什么我告诉您您的解决方案在您当前的环境中可能没问题。 但最好为任何变化做好准备,其中一些问题应该得到解决。

PS:关于C#或Java,你应该记住,这些语言并没有按照你所说的去做。 它们实际上是独立编译文件(如cpp文件)并为每个文件全局存储接口。 然后,这些接口(以及任何其他链接的接口)用于链接整个项目,这就是它们能够处理循环引用的原因。 由于 C++ 每个文件只执行一次编译,因此它无法全局存储接口。 这就是为什么您需要将它们明确地写入头文件中。

You are right to say that your solution works. It may even have no cons for your current project and developing environment.

But...

As others stated, putting all your code in header files forces a full compilation every time you change one line of code. This may not be an issue yet but your project may grow large enough to the point compilation time will be an issue.

Another problem is when sharing code. While you may not be directly concerned yet, it is important to keep as much code as possible hidden from a potential user of your code. By putting your code into the header file, any programmer using your code must look the whole code, while there are just interested in how to use it. Putting your code in the cpp file allows to only deliver a binary component (a static or dynamic library) and its interface as header files, which may be simpler in some environment.

This is a problem if you want to be able to turn your current code into a dynamic library. Because you don't have a proper interface declaration decoupled from the actual code, you won't be able to deliver a compiled dynamic library and its usage interface as readable header files.

You may not have these issues yet, that's why I was telling that your solution may be ok in your current environment. But it is always better to be prepared to any change and some of these issues should be addressed.

PS: About C# or Java, you should keep in mind that these languages are not doing what you say. They are actually compiling files independently (like cpp files) and stores the interface globally for each file. These interfaces (and any other linked interfaces) are then used to link the whole project, that's why they are able to handle circular references. Because C++ does only one compilation pass per file, it is not able to globally store interfaces. That's why you are required to write them explicitely in header files.

巨坚强 2024-07-13 17:10:06

对我来说,明显的缺点是您总是必须立即构建所有代码。 使用 .cpp 文件,您可以进行单独编译,因此您只需重建真正发生更改的部分。

The obvious downside to me is that you always have to build all the code at once. With .cpp files, you can have separate compilation, so you're only rebuilding bits that have really changed.

爱已欠费 2024-07-13 17:10:06

您误解了该语言的用途。 .cpp 文件实际上(或者应该是除了内联代码和模板代码之外)系统中唯一的可执行代码模块。 .cpp 文件被编译成目标文件,然后链接在一起。 .h 文件仅用于前向声明 .cpp 文件中实现的代码。

这会带来更快的编译时间和更小的可执行文件。 它看起来也相当干净,因为您可以通过查看其 .h 声明来快速了解您的类。

至于内联代码和模板代码 - 因为它们都用于由编译器而不是链接器生成代码 - 它们必须始终可供每个 .cpp 文件的编译器使用。 因此,唯一的解决方案是将其包含在您的 .h 文件中。

但是,我开发了一个解决方案,其中我的类声明在 .h 文件中,所有模板和内联代码在 .inl 文件中,所有非模板/内联代码的实现在我的 .cpp 文件中。 .inl 文件 #included 在我的 .h 文件的底部。 这使事情保持干净和一致。

You misunderstand how the language was intended to be used. .cpp files are really (or should be with the exception of inline and template code) the only modules of executable code you have in your system. .cpp files are compiled into object files that are then linked together. .h files exist solely for forward declaration of the code implemented in .cpp files.

This results in quicker compile time and smaller executable. It also looks considerably cleaner because you can get a quick overview of your class by looking at its .h declaration.

As for inline and template code - because both of these are used to generate code by the compiler and not the linker - they must always be available to the compiler per .cpp file. Therefore the only solution is to include it in your .h file.

However, I have developed a solution where I have my class declaration in a .h file, all template and inline code in a .inl file and all implementation of non template/inline code in my .cpp file. The .inl file is #included at the bottom of my .h file. This keeps things clean and consistent.

当爱已成负担 2024-07-13 17:10:06

您的方法的一个缺点是您无法进行并行编译。 您可能认为现在编译速度更快,但如果您有多个 .cpp 文件,您可以在自己计算机上的多个内核上并行构建它们,或者使用 distcc 或 Incredibuild 等分布式构建系统。

One disadvantage of your approach is you can't do parallel compilation. You might think you're getting a faster compile now, but if you have multiple .cpp files you can build them in parallel on either multiple core on your own machine or using a distributed build system like distcc or Incredibuild.

伤痕我心 2024-07-13 17:10:06

您可能想查看Lazy C++。 它允许您将所有内容放在一个文件中,然后在编译之前运行并将代码拆分为 .h 和 .cpp 文件。 这可能会为您提供两全其美的选择。

编译时间缓慢通常是由于用 C++ 编写的系统内的过度耦合造成的。 也许您需要将代码拆分为具有外部接口的子系统。 这些模块可以在单独的项目中编译。 这样可以最大限度地减少系统不同模块之间的依赖关系。

You might want to check out Lazy C++. It allows you to place everything in a single file and then it runs prior to compilation and splits the code into .h and .cpp files. This might offer you the best of both worlds.

Slow compile times are usually due to excessive coupling within a system written in C++. Maybe you need to split code into subsystems with external interfaces. These modules could be compiled in separate projects. This way you can minimize the dependency between different modules of the system.

仅一夜美梦 2024-07-13 17:10:06

我喜欢考虑在接口和实现方面分离 .h 和 .cpp 文件。 .h 文件包含另外一个类的接口描述,.cpp 文件包含实现。 有时,一些实际问题或清晰度会妨碍完全干净的分离,但这就是我开始的地方。 例如,为了清晰起见,我通常在类声明中内联编写小型访问器函数。 较大的函数编码在 .cpp 文件中。

无论如何,不​​要让编译时间决定您将如何构建程序。 最好有一个可读且可维护的程序,而不是 1.5 分钟而不是 2 分钟即可编译的程序。

I like to think about separation of .h and .cpp files in terms of interfaces and implementations. The .h files contain the interface descriptions to one more classes and the .cpp files contain the implementations. Sometimes there are practical issues or clarity that prevent a completely clean separation, but that's where I start. For example, small accessor functions I typically code inline in the class declaration for clarity. Larger functions are coded in the .cpp file

In any case, don't let compilation time dictate how you will structure your program. Better to have a program that's readable and maintainable over one that compiles in one 1.5 mintues instead of 2 minutes.

胡大本事 2024-07-13 17:10:06

你要放弃的一件事是匿名命名空间,如果没有它我将很难生活。

我发现它们对于定义特定于类的实用函数非常有价值,这些函数在类的实现文件之外应该不可见。 它们还非常适合保存对系统其他部分不可见的任何全局数据,例如单例实例。

One thing you're giving up that I would have a hard time living without is anonymous-namespaces.

I find that they're incredibly valuable for defining class-specific utility functions that should invisible outside the class's implementation file. They're also great for holding any global data that should be invisible to the rest of the system,like a singleton instance.

铁憨憨 2024-07-13 17:10:06

您超出了该语言的设计范围。 虽然你可能会得到一些好处,但它最终会咬你的屁股。

C++ 是为具有声明的 h 文件和具有实现的 cpp 文件而设计的。 编译器就是围绕这个设计构建的。

是的,人们争论这是否是一个好的架构,但这就是设计。 最好将时间花在解决问题上,而不是重新发明设计 C++ 文件架构的新方法。

You're going outside of the design scope of the language. While you may have some benefits, it's going to eventually bite you in the butt.

C++ is designed for h files having declarations, and cpp files having implementations. Compilers are built around this design.

Yes, people debate whether that's a good architecture, but it's the design. It's better to spend your time on your problem than reinventing new ways to design C++ file architecture.

薯片软お妹 2024-07-13 17:10:06

好吧,正如许多人指出的那样,这个想法有很多缺点,但为了平衡一点并提供一个优点,我想说,将一些库代码完全放在标头中是有意义的,因为它将使其独立于其他库代码 代码

例如,如果尝试使用不同的开源库,则可以将它们设置为使用不同的方法来链接到您的程序 - 有些可能使用操作系统动态加载的库代码,有些可能使用操作系统的动态加载库 设置为静态链接; 有些可能设置为使用多线程,而另一些则不然。 对于程序员来说,尝试解决这些不兼容的方法很可能是一项艰巨的任务,尤其是在时间有限的情况下。

然而,当使用完全包含在标头中的库时,所有这些都不是问题。 对于一个合理且编写良好的库来说,“它确实有效”。

Well, as many have pointed out there is a lot of cons to this idea, but to balance out a bit and provide a pro, I would say that having some library code entirely in headers makes sense, since it will make it independent of other settings in the project it is used in.

For example, if one is trying to make use of different Open Source libraries they can be set to use different approaches for linking to your program - some may use the operating system's dynamically loaded library code, others is set to be statically linked; some may be set to use multithreading, while others is not. And it may very well be an overwhelming task for a programmer - especially with a time constraint - to try to sort these incompatible approches out.

All of that is however not an issue when using libraries that are contained entirely in headers. "It just works" for a reasonable well-written library.

羁拥 2024-07-13 17:10:06

我相信,除非您使用 MSVC 的预编译头,并且您使用 Makefile 或其他基于依赖项的构建系统,否则在迭代构建时拥有单独的源文件应该编译得更快。 因为我的开发几乎总是迭代的,所以我更关心它重新编译我在文件 x.cpp 中所做的更改的速度,而不是我未更改的其他二十个源文件。 此外,我对源文件的更改频率比对 API 的更改频率要高得多,因此它们的更改频率较低。

关于循环依赖。 我会更进一步采纳帕塞巴尔的建议。 他有两个互相有指针的类。 相反,我更频繁地遇到一个类需要另一类的情况。 发生这种情况时,我将依赖项的头文件包含在另一个类的头文件中。 一个例子:

// foo.hpp
#ifndef __FOO_HPP__
#define __FOO_HPP__

struct foo
{
   int data ;
} ;

#endif // __FOO_HPP__

// bar.hpp
#ifndef __BAR_HPP__
#define __BAR_HPP__

#include "foo.hpp"

struct bar
{
   foo f ;
   void doSomethingWithFoo() ;
} ;
#endif // __BAR_HPP__

// bar.cpp
#include "bar.hpp"

void bar::doSomethingWithFoo()
{
  // Initialize f
  f.data = 0;
  // etc.
}

我之所以包含这个(与循环依赖关系稍微无关),是因为我觉得除了随意包含头文件之外,还有其他选择。 在此示例中,struct bar 源文件不包含 struct foo 头文件。 这是在头文件中完成的。 这样做的优点是,使用 bar 的开发人员不必了解开发人员使用该头文件需要包含的任何其他文件。

I believe that unless you are using MSVC's pre-compiled headers, and you are using a Makefile or other dependency based build system, having separate source files should compile faster when building iteratively. Since, my development is almost always iterative, I care more about how fast it can recompile the changes I made in file x.cpp than in the twenty other source files that I didn't change. Additionally, I make changes much more frequently to the source files than I do to the APIs so they change less frequently.

Regarding, circular dependencies. I would take paercebal's advice one step farther. He had two classes that had pointers to each other. Instead, I run into the case more frequently where one class requires another class. When this happens, I include the header file for the dependency in the header file of the other class. An example:

// foo.hpp
#ifndef __FOO_HPP__
#define __FOO_HPP__

struct foo
{
   int data ;
} ;

#endif // __FOO_HPP__

.

// bar.hpp
#ifndef __BAR_HPP__
#define __BAR_HPP__

#include "foo.hpp"

struct bar
{
   foo f ;
   void doSomethingWithFoo() ;
} ;
#endif // __BAR_HPP__

.

// bar.cpp
#include "bar.hpp"

void bar::doSomethingWithFoo()
{
  // Initialize f
  f.data = 0;
  // etc.
}

The reason that I include this, which is slightly unrelated to circular dependencies, is that I feel there are alternatives to including header files willy-nilly. In this example the struct bar source file does not include the struct foo header file. This is done in the header file. This has an advantage in that a developer using bar does not have to know about any other files that the developer would need to include to use that header file.

天暗了我发光 2024-07-13 17:10:06

标头中的代码的一个问题是它必须内联,否则在链接包含同一标头的多个翻译单元时将会遇到多重定义问题。

最初的问题指定项目中只有一个 cpp,但如果您要创建用于可重用库的组件,则情况并非如此。

因此,为了尽可能创建最可重用和可维护的代码,仅将内联和可内联代码放在头文件中。

One problem with code in headers is that it must be inlined, otherwise you will have multiple-definition problems when linking multiple translation units that include that same header.

The original question specified that there was only ever a single cpp in the project, but that is not the case if you're creating a component destined for a reusable library.

Therefore, in the interests of creating the most reusable and maintainable code as possible, only put inlined and inlinable code in header files.

独享拥抱 2024-07-13 17:10:06

面向对象编程的重要理念在于数据隐藏导致封装类的实现对用户隐藏。 这主要是为了提供一个抽象层,类的用户主要使用实例特定类型和静态类型的可公开访问的成员函数。 然后,类的开发人员可以自由修改实际的实现,前提是这些实现不暴露给用户。 即使实现是私有的并在头文件中声明,更改实现也将需要重新编译所有依赖的代码库。 然而,如果实现(成员函数的定义)位于源代码(非头文件)中,则库会发生更改,并且依赖的代码库需要与库的修订版本重新链接。 如果该库像共享库一样动态链接,那么保持函数签名(接口)相同并且实现更改也不需要重新链接。 优势? 当然。

The important philosophy of Object Oriented Programming lies in having data hiding leading to encapsulated classes with implementation hidden out from users. This is primarily to provide an abstraction layer where the users of a class primarily use the publicly accessible member functions for instance specific as well as static types. Then the developer of the class is free to modify the actual implementations provided the implementations are not exposed to the users. Even if the implementation is private and declared in a header file, changing the implementation will require all dependent codebase to re-compile. Whereas, if the implementation (definition of the member functions) are in a source code (non-header file), then the library gets changed, and the dependent codebase needs to re-link with the revised version of the library. If that library is dynamically linked one like a shared library then keeping the function signature (interface) same and implementation changed does not require re-linking also. Advantage? Of course.

太阳哥哥 2024-07-13 17:10:06

静态或全局变量的拼凑甚至更不透明,也许无法调试。

例如计算分析的迭代总数。

在我的杂乱文件中,将这些项目放在 cpp 文件的顶部可以很容易地找到它们。

我所说的“也许不可调试”是指我通常会将这样的全局变量放入 WATCH 窗口中。 由于它始终在范围内,因此无论程序计数器现在位于何处,WATCH 窗口始终可以到达它。 通过将此类变量放在头文件顶部的 {} 之外,您可以让所有下游代码“看到”它们。 通过将它们放在 {} 内,如果您的程序计数器位于 {} 之外,我认为调试器将不再将它们视为“范围内”。 而对于 kludge-global-at-Cpp-top,即使它在 link-map-pdb-etc 中显示的范围内可能是全局的,但如果没有 extern 语句,其他 Cpp 文件就无法访问它,避免意外耦合。

static-or-global-variable kludges even less transparent, perhaps un-debuggable.

for example counting the total number of iterations for analysis.

In MY kludged files putting such items at the top of the cpp file makes them easy to find.

By "perhaps un-debuggable", I mean that routinely I will put such a global into the WATCH window. Since it's always-in-scope the WATCH window can always get to it no matter where the program counter happens to be right now. By putting such variables outside a {} at the top of a header file you would let all downstream code "see" them. By putting them INSIDE a {}, offhand I would think the debugger will no longer consider them "in-scope" if your program-counter is outside the {}. Whereas with the kludge-global-at-Cpp-top, even though it might be global to the extent of showing up in your link-map-pdb-etc, without an extern-statement the other Cpp files can't get to it, avoiding accidental coupling.

年华零落成诗 2024-07-13 17:10:06

没有人提出的一件事是编译大文件需要大量内存。 立即编译整个项目将需要如此巨大的内存空间,即使您可以将所有代码放入标头中也是不可行的。

One thing nobody has brought up is that compiling large files requires a lot of memory. Compiling your whole project at once would require such a huge memory space that it just isn't feasible even if you could put all code in headers.

¢蛋碎的人ぎ生 2024-07-13 17:10:06

如果您使用模板类,则无论如何都必须将整个实现放在标头中...

一次性编译整个项目(通过单个基本 .cpp 文件)应该允许诸如“整个程序优化”或“跨项目”之类的内容。模块优化”,仅在少数高级编译器中可用。 如果您将所有 .cpp 文件预编译为目标文件,然后进行链接,那么使用标准编译器实际上不可能实现这一点。

If you are using template classes, you have to put the whole implementation in the header anyways...

Compiling the whole project in one go (through a single base .cpp file) should allow something like "Whole Program Optimisation" or "Cross-Module Optimisation", which is only available in a few advanced compilers. This is not really possible with a standard compiler if you are precompiling all your .cpp files into object files, then linking.

梦归所梦 2024-07-13 17:10:06

标头中的代码可以方便编码过程。 此外,预编译能力还提高了效率。 一个副作用是,总体二进制大小通常会随着标头中代码量的增加而增加,例如,如果标头跨多个库/可执行文件使用。

然而,C++20 模块是现在的方式:
https://learn.microsoft.com/ en-us/cpp/cpp/modules-cpp?view=msvc-170

引用:

模块消除或减少了许多与
头文件的使用。

使用模块的提示:
https://github。 com/MicrosoftDocs/cpp-docs/blob/main/docs/cpp/tutorial-named-modules-cpp.md

请注意,对于模块(与预编译头不同),在实现文件中包含代码,而不是在模块接口文件,可以有编译时的好处。

关于二进制大小(以 Visual C++ 为例)模块构建生成对象 (.obj) 文件(以及其他文件,例如接口 .ifc 文件),因此二进制大小与接口文件源代码大小的比例问题不大.
https://lists.isocpp.org/sg15/att-1346/C___20_Modules_Build_in_Visual_Studio。 pdf

Code in headers can be convenient for the coding process. Also, the ability to precompile improves efficiency. One side effect is that overall binary size generally increases as the amount of code in headers increases, e.g., if the headers are used across multiple libs/exes.

However, C++20 modules are the way now:
https://learn.microsoft.com/en-us/cpp/cpp/modules-cpp?view=msvc-170

Quote:

Modules eliminate or reduce many of the problems associated with the
use of header files.

Tips for using modules:
https://github.com/MicrosoftDocs/cpp-docs/blob/main/docs/cpp/tutorial-named-modules-cpp.md

Note that for modules (unlike for precompiled headers), having code in implementation files, as opposed to the module interface file, can have compilation-time benefits.

Regarding binary size (using Visual C++ as an example) module builds produce object (.obj) files (as well as other files such as the interface .ifc file), so binary size proportionality with interface file source code size is less of an issue.
https://lists.isocpp.org/sg15/att-1346/C___20_Modules_Build_in_Visual_Studio.pdf

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