55

What are the C++ coding and file organization guidelines you suggest for people who have to deal with lots of interdependent classes spread over several source and header files?

I have this situation in my project and solving class definition related errors crossing over several header files has become quite a headache.

Don Kirkby
  • 41,771
  • 21
  • 173
  • 252
Ashwin Nanjappa
  • 68,458
  • 72
  • 198
  • 283
  • Related question: [Your preferred C/C++ header policy for big projects?](http://stackoverflow.com/questions/181921/your-preferred-cc-header-policy-for-big-projects) – Deduplicator Dec 05 '14 at 00:06

6 Answers6

77

Some general guidelines:

  • Pair up your interfaces with implementations. If you have foo.cxx, everything defined in there had better be declared in foo.h.
  • Ensure that every header file #includes all other necessary headers or forward-declarations necessary for independent compilation.
  • Resist the temptation to create an "everything" header. They're always trouble down the road.
  • Put a set of related (and interdependent) functionality into a single file. Java and other environments encourage one-class-per-file. With C++, you often want one set of classes per file. It depends on the structure of your code.
  • Prefer forward declaration over #includes whenever possible. This allows you to break the cyclic header dependencies. Essentially, for cyclical dependencies across separate files, you want a file-dependency graph that looks something like this:
    • A.cxx requires A.h and B.h
    • B.cxx requires A.h and B.h
    • A.h requires B.h
    • B.h is independent (and forward-declares classes defined in A.h)

If your code is intended to be a library consumed by other developers, there are some additional steps that are important to take:

  • If necessary, use the concept of "private headers". That is, header files that are required by several source files, but never required by the public interface. This could be a file with common inline functions, macros, or internal constants.
  • Separate your public interface from your private implementation at the filesystem level. I tend to use include/ and src/ subdirectories in my C or C++ projects, where include/ has all of my public headers, and src/ has all of my sources. and private headers.

I'd recommend finding a copy of John Lakos' book Large-Scale C++ Software Design. It's a pretty hefty book, but if you just skim through some of his discussions on physical architecture, you'll learn a lot.

Tom
  • 10,273
  • 3
  • 39
  • 49
  • Excellent points. Suggestion: rephrase bullet 2 to say 'forward declarations or #includes', to emphasize that there is no need to include the header if a forward declaration is sufficient. – Jonathan Leffler Dec 06 '08 at 10:57
  • Good post. I most oftenly follow it. Just in the filesystem level, during development of each module I keep header files in the same directoy. Whenever the module gets to a stable release, the header along with the binary get 'installed' for the rest of modules to see into a different dir. – David Rodríguez - dribeas Dec 06 '08 at 11:02
  • I note that bullet 5 does make the point that I suggest for bullet 2, but the simple reordering of the terms would still be good, IMNSHO. – Jonathan Leffler Dec 06 '08 at 11:03
  • 1
    good post. tho i had cases where i had local utility classes defined in namespace scope in an anonymous namespace (say, function object classes overloading op()) which was defined in my .cxx file not declared in the .hpp file. – Johannes Schaub - litb Dec 06 '08 at 13:29
  • 1
    I heartily second the recommendation of John Lako's book: everyone working on sizeable C++ projects should read this book! – Stephen C. Steel Oct 04 '09 at 22:19
  • Can you add some detail on why an "everything" header is a bad idea? It seems a fairly common pattern, especially when the software in question is a shared library and not an application. – j b Aug 05 '14 at 11:50
10

Check out the C and C++ coding standards at the NASA Goddard Space Flight Center. The one rule that I specially noted in the C standard and have adopted in my own code is the one that enforces the 'standalone' nature of header files. In the implementation file xxx.cpp for the header xxx.h, ensure that xxx.h is the first header included. If the header is not self-contained at any time, then compilation will fail. It is a beautifully simple and effective rule.

The only time it fails you is if you port between machines, and the xxx.h header includes, say, <pqr.h>, but <pqr.h> requires facilities that happen to be made available by a header <abc.h> on the original platform (so <pqr.h> includes <abc.h>), but the facilities are not made available by <abc.h> on the other platform (they are in def.h instead, but <pqr.h> does not include <def.h>). This isn't a fault of the rule, and the problem is more easily diagnosed and fixed if you follow the rule.

Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
  • 1
    an 'detect-os.h' can set #define OS to win/linux etc. and #include can use pre processor or automatic string concatanation to include the correct file. ( OS "-file.h" ==> win-file.h/linux-file.h" – vrdhn Feb 06 '09 at 16:38
6

Check the header file section in Google style guide

tresf
  • 3,691
  • 3
  • 26
  • 78
yesraaj
  • 42,284
  • 65
  • 185
  • 246
5

Tom's answer is an excellent one!

Only thing I'd add is to never have "using declarations" in header files. They should only be allowed in implementation files, e.g. foo.cpp.

The logic for this is well described in the excellent book "Accelerated C++" (Amazon link - sanitised for script kiddie link nazis)

Community
  • 1
  • 1
Rob Wells
  • 34,617
  • 13
  • 76
  • 144
  • @Rob, could you tell us what section in _Accelerated C++_ one would find the explanation you mention? – jwfearn Dec 06 '08 at 19:16
  • @jwfearn i can't at the moment as my copy is temporarily in storage. i will try and find out for you though – Rob Wells Dec 11 '08 at 00:31
  • @jwfearn - my copy was actually at work, buried away with my other books. Section 4.3 is what you want, specfically a paragraph about half way down the page on P67. HTH. cheers, Rob – Rob Wells Dec 15 '08 at 18:06
3

I'd like to add one very good practice (both in C and C++) which is often forsaken :

foo.c

#include "foo.h" // always the first directive

Any other needed headers should follow, then code. The point is that you almost always need that header for this compilation unit anyway and including it as a first directive warrants the header remains self-sufficient (if it is not, there will be errors). This is especially true for public headers.

If at any point you need to put something before this header inclusion (except comments of course), then it is likely you're doing something wrong. Unless you really know what you are doing... which leads to another more crucial rule => comment your hacks !

Alex
  • 2,573
  • 22
  • 24
3

One more point in addition to the others here:

Don't include any private definitions in an include file. E.g. any definition that is only used in xxx.cpp should be in xxx.cpp, not xxx.h.

Seems obvious, but I see it frequently.

Steve Fallows
  • 5,974
  • 5
  • 44
  • 64