Multiple Classes in a Header File VS. a Single Header File Per Class

Multiple classes in a header file vs. a single header file per class

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.

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).

Should I put classes in separate files in C++?

C and C++ convention is one header file per library, or section of a library, rather than one header file per class. Try to group related classes together. So for example in the standard libraries <set> contains both std::set and std::multi_set, but then those are templates that do very similar things.

There isn't any truly consistent rule for public headers, though - Boost.asio has a big header file with a lot in it. Boost.DateTime is divided reasonably far down, although still not to the level of a single type.

You can put the implementations of the member functions in one source file per class if you like. Even one source file per function if you're following the GNU C style, although I don't think I recommend that for C++. And you could always implement your public-facing header files by having them include multiple separate headers.

It might depend as much on your version control and build processes as anything else - it's convenient to change a class without touching any files containing anything unrelated to the class, since then the bare minimum rebuilds and the changelog clearly identifies what has changed from the filename. But a class isn't necessarily the boundary of what's considered "unrelated code".

For example if you're writing collections with iterators, or classes that register listeners with some framework where the listener will act back on the main class, it doesn't make much sense to separate them. Morally speaking those are "inner classes" even if you don't implement them as nested classes in C++.

Why can classes be defined in header files?

If a header file has a definition of a class, for example ThisClass, and that header file is included in two other files for example a.cpp and b.cpp. Why wouldn't it violate the One Definition Rule?

Because, as you quoted, the one definition rule specifically allows for this.

How can it? Read on…

If a ThisClass object is created in either file, which definition would be called?

It doesn't matter, because the definitions are required to be absolutely, lexically identical.

If they're not, your program has undefined behaviour and you can expect all manner of weirdness to ensue.

Relationship between classes and header files

You may need to step back in your book a bit, but I'll explain a little bit.

Include (.h) files typically exist so that you can have 10 .cpp files that make use of the declared class. This is where you put class Foo { ... } stuff. Then anyone who wants to use Foo can include the .h file.

The .cpp files are where you implement the class. I think you understand this.

When you compile, you compile each of the .cpp's and then link them together. Or you can just do this:

g++ --std=c++17 A.cpp B.cpp C.cpp -o MyProgram

This will program MyProgram (you can call it whatever you want) and include all the code from those three .cpp files.

It can be a lot more complicated than that, of course. But if you do a g++ line that is similar to the one I shows you, listing all of your .cpp files, then as long as you've implemented all the code that you've defined, it should build.

Multiple structures defined inside a header file - Should I move them out in separate h and cpp files

When challenged with a major refactoring like this, you will most likely do one of the following approaches: refactor in bulk or do this incrementally.

The advantage of doing it in bulk is that you will go through the code very fast (compared to incrementally) however if you end up with some mistake, it can take quite a lot of time before you get it fixed.

Doing this incrementally and splitting of the classes one by one, you reduce the risk of time-consuming mistakes, however it will take some more time.

Personally, I would try to combine the 2 approaches:

  • Split off every class, one-by-one (top-to-bottom) into different translation units
  • However keeping the major include file, and replacing all moved classed by includes
  • Afterwards you can remove the includes to this major header file and replace them by the singular classes
  • Finally, remove the major header file

Something that I've already found useful for creating self-sufficient header files is to precompile your headers. This compilation will fail when you haven't included the right data.

Difference between implementing a class inside a .h file or in a .cpp file

The main practical difference is that if the member function definitions are in the body of the header, then of course they are compiled once for each translation unit which includes that header. When your project contains a few hundred or thousand source files, and the class in question is fairly widely used, this might mean a lot of repetition. Even if each class is only used by 2 or 3 others, the more code in the header, the more work to do.

If the member function definitions are in a translation unit (.cpp file) of their own, then they are compiled once, and only the function declarations are compiled multiple times.

It's true that member functions defined (not just declared) in the class definition are implicitly inline. But inline doesn't mean what people might reasonably guess it means. inline says that it's legal for multiple definitions of the function to appear in different translation units, and later be linked together. This is necessary if the class is in a header file that different source files are going to use, so the language tries to be helpful.

inline is also a hint to the compiler that the function could usefully be inlined, but despite the name, that's optional. The more sophisticated your compiler is, the better it is able to make its own decisions about inlining, and the less need it has for hints. More important than the actual inline tag is whether the function is available to the compiler at all. If the function is defined in a different translation unit, then it isn't available when the call to it is compiled, and so if anything is going to inline the call then it's going to have to be the linker, not the compiler.

You might be able to see the differences better by considering a third possible way of doing it:

// File class.h
class MyClass
{
private:
//attributes
public:
void method1(...);
void method2(...);
...
};

inline void MyClass::method1(...)
{
//implementation
}

inline void MyClass::method2(...)
{
//implementation
}

Now that the implicit inline is out of the way, there remain some differences between this "all header" approach, and the "header plus source" approach. How you divide your code among translation units has consequences for what happens as it's built.

Is it possible to define multiple classes in just one .cpp file?

There is no rule you have to follow (like in java). You're free to place and name classes in however named files you like (besides the suffix).

However its another question if that is good practices (its not!).

Header files setup when using c++ class in multiple source/header files

Make sure you include <vector> before including other headers that contain std::vector<Something>. For instance, in globals.cpp add #include <vector> before #include "globals.h".

Also, you can't use a forward-declared struct/class type like Policy from globals.h in types that require to know its size (like std::vector<Policy>). std::vector<*Policy> would work though.



Related Topics



Leave a reply



Submit