Introduction
Perhaps one of the reasons C++ has been so popular is its ability to serve small, large, and massive projects well. You can write a few classes for a small prototype or research project, and as the project grows and people are added, C++ will allow you to scale the application into modules that have varying degrees of independence. The trade-off is that you have to make time to do some manual reorganization along the way (adding namespaces, rearranging your header files' physical locations, etc.). Usually this is worth it though, because you can make your application modular and let different people focus only on their logical, functional areas.
The manual labor that you have to invest along the way is inversely proportional to the amount of time you spend designing modularity in the first place. Start with some of the good techniques for modularization, and your code base will scale.
If you don't already use namespaces, you've probably at least heard of them, and very likely you use one already: the std namespace, which is the namespace that contains the standard library. Namespaces are not used as frequently as they ought to be, in my experience, but that's not because they're complicated or using them requires much effort. Recipe 2.3 explains how to modularize code with namespaces.
Many of the recipes in this chapter describe techniques that you apply from within header files. Since there are a number of facilities discussed, each explaining a different part of a header file, I have included Example 2-1 in the introduction, which shows what a typical header file looks like that uses all of the techniques described in this chapter.
Example 2-1. A header file
#ifndef MYCLASS_H_ _ // #include guards, Recipe 2.0 #define MYCLASS_H_ _ #include namespace mylib { // Namespaces, Recipe 2.3 class AnotherClass; // forward class declarations, Recipe 2.2 class Logger; extern Logger* gpLogger; // External storage declaration, Recipe 2.1 class MyClass { public: std::string getVal( ) const; // ... private: static int refCount_; std::string val_; } // Inline definitions, Recipe 2.4 inline std::string MyClass::getVal( ) const { return(val_); } #include "myclass.inl" } // namespace #endif // MYCLASS_H_ _
Once you have your header file written and out of the way, most of the time you will need an implementation file, too, by which I mean a .cpp file that contains definitions and not just declarations. There is less that goes in an implementation file than there is in a header file, but for the sake of completeness, Example 2-2 contains a sample implementation file that goes with the header file presented in Example 2-1.
Example 2-2. An implementation file
#include "myclass.h" namespace mylib { MyClass::refCount_ = 0; // Static definition, Recipe 8.4 MyClass::foo( ) { // Method implementations // ... } }
Of course, your implementation files will no doubt be full of thoughtful, well-written comments, too, but I left that out for the sake of clarity.