Pros and cons of placing all the code in header files in C ++?

You can structure your C ++ program so that (almost) all the code is in the header files. It essentially looks like a C # or Java program. However, you need at least one .cpp file to pull out all the header files during compilation. Now I know that some people will absolutely hate this idea. But I did not find any convincing flaws in this. I can point out some advantages:

[1] Faster compilation time. All header files are processed only once, because there is only one .cpp file. In addition, one header file cannot be included more than once, otherwise you will get a build break. There are other ways to achieve faster compilation using an alternative approach, but it's that simple.

[2] He avoids circular dependencies, making them absolutely clear. If ClassA in ClassA.h has a circular dependency on ClassB in ClassB.h , I have to put the front link, and it sticks out. (Note that this is not like C # and Java, where the compiler automatically resolves circular dependencies, which encourages incorrect IMO encoding methods). Again, you can avoid circular dependencies if your code was in .cpp files, but in a real project .cpp files tend to include random headers until you can determine who depends on whom.

Your thoughts?

+43
c ++ header-files architecture compilation circular-dependency
Oct 11 '08 at 8:44
source share
17 answers

Reason [1] Faster compilation time

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

Perhaps you should split your project into more logical sources / headers: to modify a class A implementation, you do NOT need to recompile class B, C, D, E implementations, etc.

Reason [2] He avoids circular dependencies

Circular dependencies in code?

Sorry, but I still have a problem that is a real problem: let them say that 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 solve the problem is to split this source into at least one source / header for each class (similar to the Java method, but with one source and one header for each 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. */ } 

So there is no dependency problem and still fast compilation time.

Did I miss something?

When working on real-world projects

in a real project, cpp files usually include random headers until you can figure out who depends on whom

Sure. But if you have time to reorganize these files to create your “single CPP”, you have time to clear these headers. My rules for headings:

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

In any case, all headers should be self-sufficient, which means:

  • The header includes all the necessary headers (and only the necessary headers - see above).
  • an empty CPP file including one header should compile without having to include anything else

This will fix ordering issues and circular dependencies.

Is compilation time a problem? Then...

If compile time is really a problem, I would consider either:

Conclusion

What you do does not put everything in the headers.

Basically you include all your files in one and only one final source.

Perhaps you are winning in terms of compiling the complete project.

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

When coding, I know that I often compile small changes (if only so that the compiler checks my code), and then, for the last time, make a complete change to the project.

I would have lost a lot of time if my project had been organized.

+30
Oct 11 '08 at 15:14
source share

I do not agree with point 1.

Yes, there is only one .cpp, and the built-in time from scratch is faster. But you rarely build from scratch. You make small changes, and he will need to recompile the whole project every time.

I prefer to do it the other way around:

  • save public announcements in .h files.
  • save definition for classes that are used only in one place in .cpp files

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

But, "maintaining the element in .h" is suitable, when developing the system, because of point 2. you did. I usually do this while building a class hierarchy, and later, when the code architecture becomes stable, I move the code to .cpp files.

+24
Oct 11 '08 at 8:50
source share

You are right to say that your solution works. This may not even have flaws for your current project and development environment.

But...

As others have argued, every code in the header files forces a full compilation every time you change one line of code. This may not be a problem yet, but your project may become large enough for point compilation time to be a problem.

Another problem is code sharing. Although you may not be interested in this, it is important to keep as much code as possible from a potential user of your code. Having placed your code in the header file, any programmer using your code should look at all the code, but just wonder how to use it. Inserting your code into a cpp file allows you to deliver only a binary component (static or dynamic library) and its interface in the form of header files, which may be easier in some environments.

This is a problem if you want your current code to become a dynamic library. Since you do not have a proper declaration of an interface other than the actual code, you will not be able to deliver the compiled dynamic library and its use interface in the form of readable header files.

You may not have a problem, so I said that your solution may be fine in your current environment. But it is always better to be prepared for any changes, and some of these problems should be addressed.

PS: About C # or Java, you should keep in mind that these languages ​​do not do what you say. They actually compile files independently (e.g. cpp files) and store an interface worldwide for each file. These interfaces (and any other related interfaces) are then used to link the entire project, so they can handle circular references. Since C ++ only performs one compilation pass per file, it cannot globally store interfaces. Therefore, you must write them explicitly in the header files.

+16
Oct. 11 '08 at 9:38
source share

You misunderstand how the language should have been used .. cpp files are really (or should be, with the exception of the built-in and boilerplate code) the only executable code modules that you have on your system .. cpp files are compiled into object files, which are then linked together with the other ... h files exist solely for the direct declaration of code implemented in .cpp files.

This results in faster compilation times and less execution. It also looks significantly cleaner because you can get a brief overview of your class by looking at its .h declaration.

As for inline and boilerplate code — because they are both used to generate code by the compiler, not the linker — they should always be available to the compiler on the .cpp file. Therefore, the only solution is to include it in your .h file.

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

+11
Oct 11 '08 at 9:14
source share

The obvious drawback for me is that you always need to create all the code at once. With .cpp files you can have separate compilation, so you only rebuild the bits that really changed.

+9
Oct 11 '08 at 8:47
source share

You might want to check out Lazy C ++ . It allows you to put everything in one file, and then it is executed before compilation and breaks the code into .h and .cpp files. It can offer you the best of both worlds.

Slow compilation times usually occur due to excessive communication within a C ++ system. Perhaps you need to split the code into subsystems with external interfaces. These modules can be compiled in separate projects. Thus, you can minimize the dependency between the various modules of the system.

+3
Oct 11 '08 at
source share

One thing that you refuse is that it would be hard for me to live without anonymous namespaces.

I find that they are incredibly valuable for defining class utility functions that should be invisible outside the class implementation file. They are also great for storing any global data that should be invisible to the rest of the system, such as a single instance.

+3
Oct 11 '08 at 15:55
source share

You go beyond language design. Although you may have some benefits, it will eventually bite you in the butt.

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

Yes, people are discussing whether architecture is good, but it's design. Better to spend your time on your problem than invent new ways to develop C ++ file architecture.

+3
Oct 11 '08 at 16:23
source share

One of the drawbacks of your approach is that you cannot do parallel compilation. You might think you are getting a faster compilation now, but if you have several .cpp files, you can create them in parallel on multiple cores on your own machine or using a distributed build system like distcc or Incredibuild.

+3
Oct 12 '08 at 16:25
source share

I like to think about separating .h and .cpp files in terms of interfaces and implementations. .H files contain interface descriptions for another class, and .cpp files contain implementations. Sometimes practical problems or clarity arise that prevent a completely clean separation, but where I start. For example, I typically embed small access functions for clarity in a class declaration. Larger functions are encoded in a .cpp file

In any case, do not let the compilation time determine how you structure your program. It is better to have a program that can be easily read and maintained compared to one that compiles in one 1.5 minutes instead of 2 minutes.

+2
Oct 11 '08 at 14:06
source share

I believe that if you do not use the pre-compiled MSVC headers and you use a Makefile or other dependency-based build system, the individual source files should build faster when building iteratively. Since my development is almost always iterative, I care about how quickly it can recompile the changes I made to the x.cpp file than in twenty other source files that I have not changed. In addition, I make changes much more often in the source files than in the API, so they change less often.

Regarding circular dependencies. I would take a step forward. He had two classes that had pointers to each other. Instead, I come across a situation more often when one class requires another class. When this happens, I include the header file for the dependency in the header file of another class. 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 I am including this, slightly unrelated to circular dependencies, is because I believe that there are alternatives to including header files perforce. In this example, the structure line source file does not contain the struct foo header file. This is done in the header file. This has the advantage that the developer using the panel does not need to know about any other files that the developer will need to include in order to use this header file.

+2
Oct 11 '08 at 17:58
source share

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

The original question stated that there was only one cpp in the project, but this is not the case if you are creating a component intended for a reusable library.

Thus, in the interest of creating the most reusable and supported code, perhaps just embed the inline and inline code in the header files.

+2
Oct 12 '08 at 2:31
source share

Well, as many of them pointed out that there are many shortcomings in this idea, but in order to balance and provide a professional, I would say that it makes sense to have some library code entirely in the headers, since it will do this regardless of other parameters in the project, in which it is used.

For example, if you try to use different Open Source libraries, they can be configured to use different approaches to link with your program - some can use the dynamically loaded library code of the operating system, others are installed as statically linked; some may be configured to use multithreading, while others may not. And this can be a very difficult task for the programmer - especially with a time limit - to try to sort these incompatible matches.

All this, however, is not a problem when using libraries that are fully contained in the headers. "It just works" for a reasonable well-written library.

+1
Oct 11 '08 at 16:49
source share

kludges static or global variables are even less transparent, they may not be debugged.

for example, counting the total number of iterations for analysis.

In MY kludged files that place such items at the top of the cpp file, they are easy to find.

"Maybe it's impossible to debug," I mean, I usually put such a global in the WATCH window. Since it is always in scope, the WATCH window can always get to it, regardless of where the program counter is found right now. By placing such variables outside of {} at the top of the header file, you would allow all downstream codes to "see" them. By putting them INSIDE a {}, I would think that the debugger would no longer treat them as "in-sphere" if your program counter is outside of {}. While with kludge-global-at-Cpp-top, although it can be global up to the level displayed in your link-map-pdb-etc, without an external operator, other Cpp files cannot reach it, avoiding an accidental connection.

0
Oct 11 '08 at 9:07
source share

One thing that no one has raised is that it takes a lot of memory to compile large files. Compiling your entire project right away will require such huge memory space that it is simply not possible, even if you can put all the code in the headers.

0
Oct 30 '08 at 1:16
source share

If you use template classes, you should still put the entire implementation in the header ...

Compiling an entire project at a time (through one basic .cpp file) should allow something like "Optimization of the entire program" or "Optimization of the cross-module", which is available only in a few advanced compilers. This is not entirely possible with the standard compiler if you precompile all your .cpp files into object files and then link.

0
Nov 19 '08 at 5:23
source share

An important philosophy of object-oriented programming is that hiding data leads to encapsulated classes with an implementation that is hidden from users. , - , . , . ​​ , . , ( -) ( ), , . , , () , . Advantage? Sure.

0
16 . '14 8:29
source share



All Articles