5

I'm starting to work with C++ and I come from C and Java. I know that in C is strongly advisable to write variable/types/function declarations in the header and variable/types/function definitions in the source code; so when I started coding in C++ I thought to adhere to the same convention as well.

Unluckly for me, in C++ there are several exceptions to this rule, among the others:

  • template classes;
  • inline functions;
  • constexpr functions;

This lead me to some confusion in the files: most of them are headers with *.cpp files containing few medium/big member functions.

So my question is: assuming to code only with classes, not with plain C, why can't I just put everything in the header and have no cpp files? (sort of Java style); to clarify: I will mostly have to deal with classes, template classes, at most template functions.

For example in Foo.hpp:

class Foo {
public:
 foo() {
     //body
 }
 bar() {
     //body
 }
}

instead of having the division between *.hpp and *.cpp:

//file Foo.hpp
class Foo {
public:
 foo();
 bar();
}

//file Foo.cpp
Foo::foo() {
   //body
}
Foo::bar() {
   //body
}

Of course something like static global variables will be put in .*cpp, but for my understanding they are the only thing that is required to be put in cpps (and their use is strongly discouraged as well).

Can you please tell me what are the weaknesses of this approach? Being a beginner in C++ I'm sure to have ignore some important factor. In my naive view, there is nothing that requires to be in the cpp files (assuming that I'm coding only with classes of course).

Thanks

Koldar
  • 1,317
  • 15
  • 35
  • 3
    The short answer is because the code will have to be compiled for every compilation unit in which it is included instead of only once. – Steve Oct 22 '18 at 12:08
  • you will extremely increase your compile time, that's the main goal – Arkady Oct 22 '18 at 12:08
  • Like the others said, yes/no using only header files might greatly effect the time needed to compile. Doing everything in header files also has great flexibility advantages, so header-only libraries are very popular too. – Tom de Geus Oct 22 '18 at 12:10
  • Assuming the header foo.h is included 100 times, the class Foo will be semantically checked 100 times as well. Ok, this is a big con. Aside compilation time (which is a big issue of course!) is there any program it couldn't be generated due to the adoption of this convention? Just trying to get the whole picture :) – Koldar Oct 22 '18 at 12:13
  • Since many modules may `#include` your header, everything you put in the header creates dependencies for other modules. If there are implementation details in the header, that means that modules that uses that header depend on your implementation details. This can and should be avoided by putting implementation details in separate files (a.k.a. .cpp files). – Adrian W Oct 22 '18 at 12:16
  • @GSerg: the answer you've posted is actually gold. So, in brief compilation times, circular dependencies and modularity breaking are the reasons. I'm not convinced about modularity breaking, since inline functions sorts of leak implementation details. And templates hugely leak implementation details. – Koldar Oct 22 '18 at 12:19
  • Following the same kind of reasoning, why not have all of the code in one large .cpp file? No headers at all, or other files. Just one big convenient source file. – Eljay Oct 22 '18 at 12:34
  • 2
    Another important point - Creating Libraries. Moving much of your source to .cpp files allows you to hide the implementation details, and allows the library files to be swapped out if need be. This means the API can be kept the same, and or helps keeping the header clean for users to view. Plus has benefits of speeding up builds if the libs are already built ;) – g-radam Oct 22 '18 at 12:40
  • @Eljay: I never said to put a whole application in a single header. I said to put each module (e.g., functionality, component) in an header, pretty much like java or C# put a functionality (read class) in a single file. Speaking of C++, one big "convenient" source file is likely to cease to be convenient since function definition order do matter (implciti declaration happens otherwise). I thought this approach would be "convenient" because you stop looking for a function: it was in a cpp file; damn not: it was a template so it was in the header... it's pretty frustrating! – Koldar Oct 22 '18 at 12:42
  • 1
    @AdamWilhelm this is abolutely a point! – Koldar Oct 22 '18 at 12:43
  • I never said you ever said that. I said "Following the same kind of reasoning...". No slight intended. Regardless, C++20 should have a new feature called Modules, which will allow C++ to migrate away from header files to modules. That should be a huge improvement all around. – Eljay Oct 22 '18 at 12:47
  • 1
    @Eljay oh, sorry if I misunderstood you. Awesome to know that C++20 is trying to solve it. To be honest, coming from C, the separation between header and source was really much cleaner there than in C++. – Koldar Oct 22 '18 at 12:50

1 Answers1

10

why can't I just put everything in the header and have no cpp files?

Well, you must have at least one source file, or else you have nothing to compile.

But, to answer, why shouldn't you put everything in a single source file: Because the size of that source file increases linearly to the size of the entire program (splitting into header files does not reduce the size. What is relevant is the size after pre-processing). And therefore (re-)compiling it becomes increasingly slower, and any change to any part of that source file (i.e. any part of the entire program, also the headers that are included) requires you to re-compile that source file (i.e. the entire program) again.

If you split the program into multiple source files, only those source files which are modified need to be re-compiled. For big projects, this can reduce an hour of compilation to a minute, which is a boon when the typical workflow is "edit -> compile -> debug -> edit -> compile -> ...".

Dynamic linking can further this advantage: You can simply replace a dynamic library even without re-linking (as long as the new version is ABI compatible).


For fairness, let me also answer why should you put everything in a single source file: Because it reduces the compilation time from scratch. If your workflow doesn't work with incremental re-builds, then reducing the full compilation time a bit is better than nothing. And because it allows better optimization, because a compiler cannot do inline expansion across source files (link time optimization may reduce this advantage, if you can rely on it being available).


Ideal solution is probably neither to define all functions in a single massive source file, nor is it ideal to define all functions in separate source files each. Ideal option is probably somewhere in between.

A typical convention is to have a single source file for member functions of each class, but there is no absolute reason why this convention should be followed. It is completely fine to define member functions of multiple classes in a single source file, and also fine to divide definitions member functions of one class into separate files, as long as you have an argument for doing so.


I thought this approach would be "convenient" because you stop looking for a function: it was in a cpp file; damn not: it was a template so it was in the header... it's pretty frustrating!

This is not a strong argument compared to compile time considerations. Development environments are available (even for free, and have been for decades) which allow you to jump to the definition of a function declaration (or invocation) in a fraction of a second.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 1
    I don't think the OP was asking if they should put the entire body of the source code into a single file. It was more about the benefits of splitting up a single class into .hpp and .cpp files as opposed to having a single .hpp file that contains the entire class definition, kind of like how Java and C# only have one file per class. – d512 Mar 21 '22 at 18:29
  • @d512 If you put all your definitions into header files, then after pre-processing, you will have only one translation unit that contains those all those definitions included from the headers. – eerorika Mar 21 '22 at 18:37
  • This didn't answer the OP's question. He was asking about HEADER files, not SOURCE files. – Jonathan Plumb Aug 28 '22 at 11:46
  • @JonathanPlumb The answer to OP's question can be found in the first sentence. You just have to apply a tiny bit of logic to it: If you have written only HEADER files, then you haven't any SOURCE files. Since there must be at least one SOURCE file, which you haven't written, you have nothing to comply. The answer is: No. The rest of my answer covers the benefits and disadvantages of having single SOURCE file which is closest to having no SOURCE file as is possible, versus having multiple SOURCE files. – eerorika Aug 31 '22 at 08:06
  • @eerorika, nice try — but applying a little bit of logic, as you say, you would have recognized that the OP did acknowledge there are a few things REQUIRED to be in .cpp when he said, “Of course something like static global variables will be put in .*cpp, but for my understanding they are the only thing that is required to be put in cpps (and their use is strongly discouraged as well).” Any C/++ programmer knows main() has to be in a .c/pp. Your answer literally had nothing to do with his question. Better luck next time. – Jonathan Plumb Sep 01 '22 at 14:36
  • @JonathanPlumb the compiler does not distinguish between source and header files. If you #include a file, the preprocessor copies its contents into the current file. – timgo Nov 23 '22 at 19:00
  • @timgo, the last part of what you said is correct, but the first part isn't. The COMPILER is what takes the header files and copies them into the code, essentially replacing the "include" line with the full code of the header file. Thus, the compiler DOES distinguish initially, just not once it actually starts compiling. At that point, even the .cpp doesn't matter (as you said), as it's all file-less code. See this article for a better understanding (look under The Build Pipeline: Preprocess, Compile, and Link): https://www.toptal.com/c-plus-plus/c-plus-plus-understanding-compilation – Jonathan Plumb Nov 27 '22 at 21:31