S R T B H P N

Class Relationships

There are four relationships between classes, illustrated in the diagram, below, using the Universal Modeling Language (UML):
1.
Inheritance:
When class D inherits from class B, it acquires the public interface of B. That is, all of the methods of B become methods of D. So any code that uses a pointer or reference to a B instance will continue to build and function if we replace the pointer or reference with one bound to a derived D instance.
This is know as Liskov substitution, and is one of the most useful properties of inheritance. It enables us to build very flexible applications that can easily be modified without breaking a lot of code. The Parser application is a good example.
2.
Composition:
When class C composes an instance of class P, it acquires the capabilities of P to support the implementation of its own methods. Composition is a strong ownership relationship. The C++ language directly supports the composition of all types, both primitive language types and also user defined types.
Note that classes in the managed languages, C# and Java, can compose only value types, e.g., numerical types and structs. All other reference types can only be aggregated. These reference types cannot be copied without serialization and deserialization.
Being able to compose and efficiently copy instances of user defined types is a significant advantage for C++.
3.
Aggregation:
When a class A aggregates a part P, it creates an instance of P on the native heap and refers to that with a pointer or C++ reference. This is a weaker form of ownership. The instance of P won't exist until some code in a method of A creates it on the native heap using new P. So at any time of A's lifetime the part P may or may not exist, depending on which of A's methods have been invoked.
With compositon, on the other hand, the Part P is guaranteed to exist if C exists. When any constructor of C is invoked, its first act is to create an instance of P. If you don't write code to do that in an initialization sequence, the compiler will supply code to ensure that the construction of P is effected.
4.
Using:
Class U uses an instance of a resource class R when it invokes methods of R using a pointer or instance passed to it as an argument of one of U's methods. The instance of R was created by a method of another class and passed to the U instance, so U does not own the instance of R and should do nothing to destroy or impair it.
It is remarkable that only these four simple class relationships are enought to model almost any application domain that you will use in your professional practice. The diagram below shows a set of classes, B, C, D, and U, and their relationships. It also shows objects of those classes and their layout in memory.
1.
When B composes an instance of C, B's instance contains an instance of C within its memory footprint. C is an integral part of B and B's code accesses C through C's public interface.
2.
When D derives from B, D's instance containts an instance of B within its memory footprint. B is an integral part of D and D inherits all of B's methods. D has access to all of B's protected data, but it does not have direct access to B's private data, even though that data is contained within D.
3.
When D is passed a pointer or reference to an instance of U, the U instance does not reside in the memory footprint of D, but in some other region of memory, determined by U's creator.
4.
A class may grant access, to its private members, to another class or specific function by using a friend delclaration. We try to avoid doing that as it stretches the encapsulation boundary of the class to also include the friended code. This is another form of the Using relationship.

You will find code that is structured, as in the diagram, above, in the folder ClassRelationships. This demonstration shows that the memory footprint assertions, made above, are in fact true for C++ code structured like this.

References:

CST strip