Is there any advantage here that I am missing?
Yes, there is. I, along with a large number of software projects, used to follow the practice outlined in the question. Many projects, including modern ones I work on, now take the opposite approach in a source file:
- The header file that declares the functions being defined in the source file is the very first that is included.
- Other header files from the same project are included next.
- Header files from non-standard projects (e.g., eigen, boost, Qt) are included after local headers.
- Finally, standard header files are included last.
- Out of order inclusions need to be clearly documented with regard to why the header was not included in the above order.
Ideally, all header files should be self-contained, and inclusion order should not matter. In practice, people often write header files that are not self-contained, and sometimes inclusion order does matter. To combat the first problem, that the first included file is the header file that declares the functions that are being defined in a source file creates a nice test that a header file is indeed self-contained. Compilation errors that result from that very first included file means there's a bug in that header.
To combat the second problem (an out of order inclusion is necessary), that should be viewed as a code smell. If the code smell is in your own project code, the best thing to do is do fix it. Code smells are occasionally a stinky necessary. Suppose, for example, you are using what would otherwise be a great third party library were it not for the fact that its header files are not self-contained. You use the library because all alternatives are worse, but you document the out of order inclusions.
This latter problem is becoming rarer as projects adopt inside-out inclusion practices (local first, system last). That old-style inclusion order hurts software quality.
A header normally contains declarations to tell a compiler about the functions (and classes, etc.) that exist in a library.
Most C and C++ compilers include a standard library that they'll link with by default. To link with other libraries, you typically have to tell the compiler (linker, really, at least in most cases) to link with them.
As to why there are so many headers: mostly because there are lots of declarations, many of which are only minimally related to each other. Since a header (normally) contains source code, each time you compile a file that includes a header, you may also be re-compiling the entirety of that header. This can slow down compilation considerably (and compilation speed is often a problem with C and C++ anyway).
Libraries themselves typically come in two forms: one is a "static library". This is basically just a bunch of object files collected together into a single file, often with a directory of some sort appended to make it easier to find which functions are contained in which files. The linker basically just looks through the directory to find files that will satisfy currently-unresolved external references in your code, and links them in like it would any other object file.
The second is a dynamic library (e.g., DLL on Windows, .so on Linux). This is more like an executable--a set of object files that have been pre-linked into a loadable form. When you link against one, the linker inserts some sort of reference to the dynamic library into your executable. When you execute your program, the OS' loader maps the dynamic library to your process' address space, and "fixes up" the references to the functions in the dynamic library, so they refer to the correct addresses in the library.
Best Answer
Design in general is always "the art to balance contradicting goals".
Here, you have these goals:
string.h
but you also don't want 3string.h
header files.gtk.h
makes sense since you want to build a user interface for your application (point #2). You could argue that people should include each widget individually but then, your framework will become slightly harder to use (point #1).If you look at old X11/Motif code, you will see dozens of
#include
statements at the top of each C source file. It compiles a little bit faster but it takes hours of manual work to maintain these lists.OTOH, if you put too many features in your header files, you will have too many dependencies in there and one of them might kill you (for example, when this dependency requires another framework with a certain version -> dependency hell).
I hope you understand by now that there is no solution; the problem is too complex and needs to be addressed again every time a new framework is started.
EDIT
For GTK, the
#include
statements are part of the public API. With these macros, they are free to rearrange the header files, their content, names and include order, in any way they like at any time without breaking a single existing project.