Aggregation

An aggregation is a specific type of composition where ownership between the complex object and the subobjects is implied. But, when an aggregate is destroyed, the subobjects are not destroyed.

For example, consider the math department of a school, which is made up of one or more teachers. Because the department does not own the teachers (they merely work there), the department should be an aggregate. When the department is destroyed, the teachers should still exist independently (they can go get jobs in other departments).

Because aggregations are just a special type of compositions, they are implemented almost identically, and the difference between them is mostly semantic. In a composition, we typically add our subclasses to the composition using either normal variables or pointers where the allocation and deallocation process is handled by the composition class.

In an aggregation, we also add other subclasses to our complex aggregate class as member variables. However, these member variables are typically either references or pointers that are used to point at objects that have been created outside the scope of the class. Consequently, an aggregate class usually either takes the objects it is going to point to as constructor parameters, or it begins empty and the subobjects are added later via access functions or operators.

Because these subclass objects live outside of the scope of the class, when the class is destroyed, the pointer or reference member variable will be destroyed, but the subclass objects themselves will still exist.

Let’s take a look at our Teacher and Department example in more detail.

01.#include <string>

02.using namespace std;

03.

04.class Teacher

05.{

06.private:

07. string m_strName;

08.public:

09. Teacher(string strName)

10. : m_strName(strName)

11. {

12. }

13.

14. string GetName() { return m_strName; }

15.};

16.

17.class Department

18.{

19.private:

20. Teacher *m_pcTeacher; // This dept holds only one teacher

21.

22.public:

23. Department(Teacher *pcTeacher=NULL)

24. : m_pcTeacher(pcTeacher)

25. {

26. }

27.};

28.

29.int main()

30.{

31. // Create a teacher outside the scope of the Department

32. Teacher *pTeacher = new Teacher("Bob"); // create a teacher

33. {

34. // Create a department and use the constructor parameter to pass

35. // the teacher to it.

36. Department cDept(pTeacher);

37.

38. } // cDept goes out of scope here and is destroyed

39.

40. // pTeacher still exists here because cDept did not destroy it

41. delete pTeacher;

42.}

In this case, pTeacher is created independetly of cDept, and then passed into cDept’s constructor. Note that the department class uses an initialization list to set the value of m_pcTeacher to the pTeacher value we passed in. When cDept is destroyed, the m_pcTeacher pointer destroyed, but pTeacher is not deallocated, so it still exists until it is independently destroyed.

To summarize the differences between composition and aggregation:

Compositions:

    • Typically use normal member variables

    • Can use pointer values if the composition class automatically handles allocation/deallocation

    • Responsible for creation/destruction of subclasses

Aggregations:

    • Typically use pointer variables that point to an object that lives outside the scope of the aggregate class

    • Can use reference values that point to an object that lives outside the scope of the aggregate class

    • Not responsible for creating/destroying subclasses

It is worth noting that the concepts of composition and aggregation are not mutually exclusive, and can be mixed freely within the same class. It is entirely possible to write a class that is responsible for the creation/destruction of some subclasses but not others. For example, our Department class could have a name and a teacher. The name would probably be added to the department by composition, and would be created and destroyed with the department. On the other hand, the teacher would be added to the department by aggregate, and created/destroyed independently.

It is also possible to create other hybrid aggregate/composition schemes, such as where a class holds independent subobjects like an aggregate, but will destroy them when the class goes out of scope like a composition.

While aggregates can be extremely useful (which we will see more of in the next lesson on container classes), they are also potentially dangerous. As noted several times, aggregates are not responsible for deallocating their subobjects when they are destroyed. Consequently, if there are no other pointers or references to those subobjects when the aggregate is destroyed, those subobjects will cause a memory leak. It is up to the programmer to ensure that this does not happen. This is generally handled by ensuring other pointers or references to those subobjects exist when the aggregate is destroyed.