Pregunta

I'm kind of new to large scale software development in C++, and I was wondering on the design side of things.

I was reading this question, and I thought that, overall, once we get past constant definitions and other trivial matters, C++ header files are just API definitions :

They define what other programmers (or yourself from other modules) will be able to use (classes, public functions), but no private class or function should be defined in it. It's like the header file defines the abstractions (interfaces ...) and the source file implements it. Once the source file is compiled, the implementation details are hidden away, and remain the publicly available headers defining what the module can do.

I felt this view of header/source file separation was a lot easier to understand and follow than the usual explanations, since you can just think "would XXX be publicly available API material, or is it a sausage-making detail to be hidden away in an undisclosed source file ?"

Is my mental model of header files roughly correct ? What did I miss ?

¿Fue útil?

Solución

It's not a bad mental model to use as a guide but unfortunately for historical / technical reasons C++ headers conflate interface and implementation in ways that this simple mental model doesn't fully capture.

In order to make good physical design decisions when things get a little more complex, it is generally necessary to understand the way headers work in C++ at a more detailed level.

To give an example: from your API view, a header file should not include the definition of a private implementation class. In practice however, such class definitions will often appear in a header file since the compiler will need to know their size if they are contained in any public classes. Techniques exist to break this kind of dependency but they generally impose a cost in terms of code complexity, performance or both. Understanding when it's appropriate to use one of these techniques requires a deeper understanding of how headers work in C++.

Otros consejos

Pretty much, there's little difference between a header file and an interface as used by other languages for the main part. Obviously though there are differences in implementation in that the header will also contain private information, but fundamentally they are used to describe the features the class or module will provide.

In a way then, its also a mental model of data structures combined with the API so it can be more useful when you're doing the kind of initial design you describe.

Ideally a header defining an API would contain just the pure interface but as C++ doesn't have an ABI, this doesn't really matter. If you build a higher level system that does provide a pure client interface, you'll be using a different mechanism such as an IDL or WSDL to define that anyway.

Header file as a pure interface works more or less fine with classes. As soon as templates come into picture, this model stops working whatsoever. Templates are not compilable, and the whole template implementation must appear in the header.

There exists a mistaken understanding that "the header contains the declarations and constants, the implementation file contains the implementation".

No. A header file should contain anything that is to be used outside the "module" == compilation unit. It is the interface between the "module" and everything else. It is what a user of your code needs to read and understand in order to use it and nothing else.

Thus any classes or functions that are part of the implementation of that interface go to the implementation file. They are complexity for which we don't want to make the user pay in cognitive terms.

To further the point, any exposed interface (declarations in the header) will get used by someone somewhere, so we can never change it without breaking distant code. Ask Scott Meyers!

As a sidenote, api documentation (/*This function fudges widgets if provided those params.*/) go to the header. Implementation documentation (/*Using Shalqlq's algorithm to fudge widget memory state.*/) go to the implementation file.

As a second sidenote, because the language prior to C++20 doesn't support python/Rust style modules, the pImpl idiom can be used to enforce the above. It's disadvantages are boilerplate code and pesky debugging.

Licenciado bajo: CC-BY-SA con atribución
scroll top