Navigation



10.3 — Aggregation

In the previous lesson on composition, you learned that compositions are complex classes that contain other subclasses as member variables. In addition, in a composition, the complex object “owns” all of the subobjects it is composed of. When a composition is destroyed, all of the subobjects are destroyed as well. For example, if you destroy a car, it’s frame, engine, and other parts should be destroyed as well. If you destroy a PC, you would expect it’s RAM and CPU to be destroyed as well.

Aggregration

An aggregation is a specific type of composition where no ownership between the complex object and the subobjects is implied. 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.

#include <string>
using namespace std;

class Teacher
{
private:
    string m_strName;
public:
    Teacher(string strName)
        : m_strName(strName)
    {
    }

    string GetName() { return m_strName; }
};

class Department
{
private:
    Teacher *m_pcTeacher; // This dept holds only one teacher

public:
    Department(Teacher *pcTeacher=NULL)
        : m_pcTeacher(pcTeacher)
    {
    }
};

int main()
{
    // Create a teacher outside the scope of the Department
    Teacher *pTeacher = new Teacher("Bob"); // create a teacher
    {
        // Create a department and use the constructor parameter to pass
        // the teacher to it.
        Department cDept(pTeacher);

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

    // pTeacher still exists here because cDept did not destroy it
    delete pTeacher;
}

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.

10.4 — Container classes
Index
10.2 — Composition

28 comments to 10.3 — Aggregation

  • Zafer

    In the Teacher and Department example, the constructor name is not same as the class name. Also when declaring an object of that class a third name is used.

  • Chris Buck

    Could you pls elaborate on what’s the difference between pcTeacher and pTeacher as far as hungarian notation is concerned?
    Wasn’t the ‘c’ used to clarify that it’s an instance of a class and didn’t we omit it when it’s a pointer to a class?

    Like shouldn’t the ‘c’ be omitted in both cases and both pointers just be named pTeacher?

    • It’s really personal preference how you do your hungarian notation. When it comes to pointers (or references), I use p (or r), then the letter denoting the type of the class, then the variable name. Thus, a pointer to a class would be prefixed pc. But however you decide to proceed is up to you (or your company). Just be consistent.

  • Tom

    typo: “When cDept is destroyed, the m_pcTeacher pointer is destroyed”

    Where does the Teacher variable type get declared? Anywhere outside the aggregate class?

    • Yep. It’s just another class defined somewhere else. Probably in another header that’s included by Department.cpp.

      Edit: I went ahead and defined a Teacher class just above the Department class so people can compile the example if they want.

  • [...] Posts « A.5 — Debugging your program (watching variables and the call stack) | Home | 10.3 — Aggregation » Tuesday, December 4th, 2007 at 2:55 [...]

  • Stuart

    Alex, I don’t understand why the pointer pcTeacher is assigned NULL as a Department constructor parameter, because it assigns to the address of the pTeacher pointer when an object of Department is instantiated. It seems like it’s never a null pointer. (?)

    • It’s assigned NULL as a default parameter just in case the programmer wants to create a department with no teacher. For example:

      Department cDept;
      

      In this case cDept would call the Department constructor with pcTeacher set to NULL. This NULL value would then be assigned to cDept.m_pcTeacher by the constructor.

  • Prateek Jain

    How do you handle the situation when subclass object gets destroyed? In our case, if the teacher dies for some reason (say heart attack:-) in case of real world situation), the pTeacher pointer will still have some value but that value will no longer be valid. If Department class has some member which access pTeacher, this could give rise to some potential problem.
    If you somehow can pass a message to Department class, it can make pTeacher equal to NULL but checking (pTeacher == NULL) everywhere in Department class doesn’t seem to be a good idea.
    How do you incorporate it into your design?

  • shouldn’t

    int main

    return 0?

  • f117f117f117677

    Hi: Alex
    as you said
    there are two pointers point to Teacher(“Bob”). pTeacher and m_pcTeacher

    if aggregate class is destroyed then m_pcTeacher is destroyed. So
    teacher(“Bob”) is gone(died).

    but delete pTeacher again? is this problem? pTeacher become a wild pointer?

  • serenity

    f117f117f117677: “class Department” doesn’t actaully delete the teacher; the only thing removed when it’s destroyed is its pointer to the teacher. The pointer in main() remains and is still valid, so there is no wild pointer and no double delete.

  • auasp

    the heading of this lesson is Aggregration.
    (one extra r)

  • Amit

    Thanks for teh description but there are couple of loose ends:
    1. How is association different from aggregation and composition ? You may want to add a section on that
    2. If a class has a data member(only one) which points to another object but this is not allocated/dealloacted in constructor and destructor. Will this be aggregation or composition ? For teh relation to be aggregation is it necessary that you should have more that one object of the same type ?

  • hector

    I love the new look of the site yo :)

  • mccp13

    I still don’t get the pointer *m_pcTeacher. when m_pcTeacher recieves pTeacher, it means that m_pTeacher and pTeacher are pointing to the same address. So when m_pcTeacher is destroyed doesn’t that mean that the address to which it is pointing to is freed, so when pTeacher gets the contents of the address, it could have changed since it is already free and ready to be used by some other program…???

  • maxatan

    Hi ! and congratulations for your job !

    However, I have a question : What would be the code with more than one teacher belonging to the Department ? How will you implement it ? With a list of pointers to Teachers (list) ?

    Thank you in advance for your advice

  • [...] : Object composition Aggregation Composition Aggregation versusComposition aggregation composition uml 2013-09-27 ???? [...]

  • Muddasar Azeem

    #include

    #include

    using namespace std;

    class Time

    { //Time class

    public:

    Time()

    { //default constructor

    hr = 0;

    min = 0;

    }

    Time(int hours, int minutes)

    { //class time constructor that accepts parameters

    if(0 <= hours && hours < 24)//makes sure hours are valid

    hr = hours;

    else

    hr = 0;

    if(0 <= minutes && minutes < 60)//makes sure minutes are valid

    min = minutes;

    else

    min = 0;

    }

    Time setTime(int hours, int minutes)

    { //sets a valid time

    if(0 <= hours && hours < 24)

    hr = hours;

    else

    hr = 0;

    if(0 <= minutes && minutes < 60)

    min = minutes;

    else

    min = 0;

    }

    Time getTime(int& hours, int& minutes)

    {

    //returns the hours and minutes

    hr = hours;

    min = minutes;

    }

    Time printTime()

    {

    //displays the hours and minutes to the screen

    if(hr < 10)

    cout << "0";

    cout << hr << ":";

    if(min < 10)

    cout << "0";

    cout << min < 23)

    hr = 0;

    }

    Time incrementMinutes()

    { //increments minutes by one

    min++;

    if(min > 59)

    {

    min = 0;

    incrementHours();

    }

    }

    private:

    int hr;

    int min;

    };

    class Date

    {//Date class

    public:

    Date()

    {//default constructor

    month = 1;

    day = 1;

    year = 1900;

    }

    Date(int m, int d, int y)

    {//constructor that accepts parameters

    if(m >= 1 && m = 1 && d = 1900 && y = 1 && m = 1 && d = 1900 && y <= 2010)

    year = y;

    else

    year = 1900;

    }

    Date getDate(int& m, int& d, int& y)

    {//returns the month, day and year

    month = m;

    day = d;

    year = y;

    }

    Date printDate()

    {//displays the month, day and year to the screen

    if(month < 10)

    cout << "0";

    cout << month << "/";

    if(day < 10)

    cout << "0";

    cout << day << "/";

    cout << year;

    }

    private:

    int month;

    int day;

    int year;

    };

    class Event

    {//Event class
    private:

    string eventName;

    Time eventTime;

    Date eventDay;

    public:

    Event(int hours = 0, int minutes = 0, int m = 1,

    int d = 1, int y = 1900, string name = "Christmas");

    Event(int hours, int minutes, int m, int d, int y, string name)
    : eventTime(hours, minutes), eventDay(m, d, y)

    {

    eventName = name;

    }

    Event setEventData(int hours, int minutes, int m, int d, int y, string name)

    {

    eventTime.setTime(hours, minutes);

    eventDay.setDate(m, d, y);

    eventName = name;

    }

    Event printEventData()

    {

    cout << eventName << " occurs ";

    eventDay.printDate();

    cout << " at ";

    eventTime.printTime();

    cout << endl;

    }

    };
    int main()

    {//instantiate an object and set data for Christmas

    Event object;

    object.setEventData(6, 0, 12, 25, 2010, "Christmas");

    //print out the data for object

    object.printEventData();

    //instantiate the second object and set date for the fourth of July

    Event object2;

    object2.setEventData(1, 15, 7, 4, 2010, "Fourth of July");

    //print out the data for the second object

    object2.printEventData();
    return 0;
    }

  • Muddasar Azeem

    Hey any one tell me whats error in the EVENT Class?????????????

  • Darin

    Hi Alex,
    I want to ask how do you dereference something like
    Teacher *pTeacher = new Teacher(“Bob”);
    since I could not fully inderstand the whole aggregration example. The only thing that I was able to understande was that the classes are exchanging the adress that pTeacher holds, but I cannot actually do anything with it(since I cannot dereference it). If possible could you please explain?

You must be logged in to post a comment.