10.8.1. Building Association

Time: 00:03:30 | Download: Large, Large (CC), Small | Streaming (CC) | Slides (PDF)
Review

It's often convenient to think of association as double-ended aggregation. While this conceptualization may not always work, it's a helpful starting point. Like aggregation, association has a weak or loose binding implemented with pointers. Consequently, the program is not required to build the relationship with the constructors as it is in the case of composition. The program can create and break the association relationship binding the related objects whenever convenient. Programmers may choose to build the relationship with setter or constructor functions as best fits the problem.

Creating Association with Setter Functions

Setter functions allow programmers to establish or update the pointer variables implementing the association relationship. The program can create the associated objects on the stack or the heap. One of association's major challenges - one of the characteristics making it more difficult than aggregation to implement - is the necessity of changing both pointers when updating the relationship. Failing to change one pointer will result in incoherency and break the relationship's bidirectionality.

project.hcontractor.h
class contractor;		// forward declaration

class project
{
    private:
        contractor* theContractor;

    public:
        void set_contractor(contractor* a_c)
        {
            theContractor = a_c;
        }
};
class project;			// forward declaration

class contractor;
{
    private:
        project* theProject;

    public:
        void set_project(project* a_p)
        {
            theProject = a_p;
        }
};
client1.cpp (stack objects)client2.cpp (heap objects)
#include "contractor.h"
#include "project.h"

int main()
{
    project	big;
    contractor	fred;
		. . . .
    set_contractor(&fred);
    set_project(&big);
        . . . .

    return 0;
}
#include "contractor.h"
#include "project.h"

int main()
{
    project*	big = new project;
    contractor*	fred = new contractor;
		. . . .
    set_contractor(fred);
    set_project(big);
        . . . .

    return 0;
}
Creating association with setter functions. Creating an association relationship with setters requires programmers to define setter functions in both related classes. Setter arguments can be existing objects or the "ingredient" data necessary for building a new object (see Setter Options), with the former being more common. Recall that setters may not have initializer lists.

Creating Association with Constructors

While programmers are not required to build association relationships with constructors, they may. Furthermore, while programmers can break or change an association relationship whenever convenient, the relationship may persist from its creation to program termination.

project.hcontractor.h
#pragma once
#include <iostream>
using namespace std;

class contractor;			// forward dec
#include "contractor.h"

class project
{
    private:
        contractor* theContractor;

    public:
        project();			// Pair a
        project(contractor* a_c);	// Pair b
};
#pragma once
#include <iostream>
using namespace std;

class project;				// forward dec
#include "project.h"

class contractor
{
    private:
        project* theProject;

    public:
        contractor(project* a_p);	// Pair a
        contractor();			// Pair b
};
project.cppcontractor.cpp
#include "project.h"

project::project()			// pair a
{
	theContractor = new contractor(this);
}

project::project(contractor* a_c)	// pair b
{
	theContractor = a_c;
}
#include "contractor.h"

contractor::contractor(project* a_p)	// pair a
{
	theProject = a_p;
}

contractor::contractor()		// pair b
{
	theProject = new project(this);
}
client.cpp
#include "contractor.h"
#include "project.h"

int main()
{
    // Pair a
    project	little;			// stack
    project*	big = new project();	// heap
    . . . .

    return 0;
}
#include "contractor.h"
#include "project.h"

int main()
{
    // Pair b
    contractor	foo;			// stack
    contractor*	bar = new contractor();	// heap
    . . . .

    return 0;
}
Creating association with constructors. C++ implements association with two pointers, one in each related class, each requiring an initializing constructor. Programmers implement the constructors as asymmetric but complementary pairs. The functions are complementary in that, while different, they work together in a function-call chain and are asymmetric in that one call begins the call chain, and the other finishes it. Only one complementary pair is necessary, but two are possible. C++ only allows prototypes in class specifications for member functions accessing an associated class.
Pair a
The client instantiates a project, calling the default constructor, which calls the parameterized contractor constructor.
Pair b
The client instantiates a contractor, calling the default constructor, which call the parameterized project constructor.