Compound Objects

Compound Objects:

There are four class relationships, doc, we use to construct Object Oriented Designs: Inheritance, Composition, Aggregation, and Using. Inheritance is an "is-a" relationship. Composition is a strong ownership or "part-of" relationship1. Aggregation is also a "part-of" relationship, but is weaker than composition because it does not guarantee that the aggregated part is owned, only that it can be owned by the aggregator. Finally, using is a dependency on an object that is not part of the user, was not created by the user, and should not be destroyed by the user.

Compound objects are composite structures created by binding together a set of classes through the relationships described above. In this discussion, we are particularly interested in the initialization of these structures.

For exposition we've selected the structure shown in the diagram below. You will find code that implements the structure with comments that help you understand what is going on and what is important.

Compound Object Demo Output
Compound Object Classes
CompoundObjects code
  • An instance of C is always contained within the footprint of each instance of B.
  • Clients of B do not see C's public interface. That is only accessible to B.
  • Similarly, an instance of B is always contained within each instance of D.
  • Since D derives from B via public inheritance, its public methods become public methods of D.
  • Note that this does not hold for constructors, destructor, or assignment operators.
  • The C++ language guarantees that a constructor call is made for the creation of every object even if the class designer did not provide constructors. It does that by generating copy constructors that do member-wise construction of the classes bases and data elements.
  • Similarly, the language guarantees that a destructor call is made for every object as it leaves the scope in which it was declared, even if the class designer did not provide a destructor. In that case the compiler generates a destructor that does member-wise destruction of each of the class's bases and member data.
  • Finally, the language guarantees that an assignment will be called, even if the class designer did not provide one. In that case the compiler generates an assignment that does member-wise assignment of the class's bases and data members.
  • The preceeding three statements hold, provided that the designer did not explicitly delete those operations. That means that the functions are post-pended with the = delete tokens. Only constructors, and assignment operators can be deleted. Code that implies the execution of any deleted members will not compile.
  • This means that you must always decide whether to provide default and copy constructors, assignment operators, and destructors for every class you design.
  • If a class's bases and member data have correct copy, assignment, and destruction semantics, then you don't need to, and should not, provide them. That is always true if your class holds only primitive types and containers from the Standard Template Library, or holds instances of classes for which that is true.
  • If your class holds pointers to data on the heap, then you must provide copy, assignment, and destruction operations, or qualify those operations with delete.
  • If you class holds any types that do not support copying or assignment, like mutexes and critical sections, then you must qualify copy, assignment, and destruction operations with delete.

  1. The managed languages C# and Java support composition only for value types, e.g., ints, doubles, ... Sometimes developers using those languages refer to the aggregated class instances as composition, but no instance of a C# or Java class can be composed. Instead handles to those instances are composed and refer to aggregated instances on the managed heap.