Discussion
Prefer forward declarations except where you really need a type's definition. You need a full definition of a class C in two main cases:
- When you need to know the size of a C object:
For example, when allocating a C on the stack or as a directly-held member of another type. - When you need to name or call a member of C:
For example, when calling a member function.
In keeping with this book's charter, we'll set aside from the start those cyclic dependencies that cause compile-time errors; you've already fixed them by following good advice present in the literature and Item 1. Let's focus on cyclic dependencies that remain in compilable code, see how they trouble your code's quality, and what steps need be taken to avoid them.In general, dependencies and their cycles should be thought of at module level. A module is a cohesive collection of classes and functions released together (see Item 5 and page 103). In its simplest form, a cyclic dependency has two classes that directly depend upon each other:class Child; // breaks the dependency cycle
class Parent {// …
Child* myChild_;
};
class Child {// … // possibly in a different header
Parent* myParent_;
};
Parent and Child depend upon each other. The code compiles, but we've set the stage for a fundamental problem: The two classes are not independent anymore, but have become interdependent. That is not necessarily bad, but it should only occur when both are part of the same module (developed by the same person or team and tested and released as a whole).Martin96a] and [Martin00] (see also Item 36): Don't make high-level modules depend on low-level modules; instead, make both depend on abstractions. If you can define independent abstract classes for either Parent or Child , you've broken the cycle. Otherwise, you must commit to making them parts of the same module.Martin98].Item 2.)