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 that we implement 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.
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.h | contractor.h |
---|---|
class contractor; // forward reference class project { private: contractor* theContractor; public: void set_contractor(contractor* a_c) { theContractor = a_c; } }; |
class project; // forward reference 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; } |
When a client calls the setters to build or modify an association, it should call the second function immediately after the first without allowing any operations on either object between the setter calls. client1.cpp demonstrates passing stack objects to setter functions (notice the address-of operator in the function calls), while client2.cpp demonstrates passing heap objects.
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. The following examples demonstrate convenient patterns I've successfully used in the past.
project.h | contractor.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.cpp | contractor.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; } |
When member functions (such as the default constructors) call functions (such as the parameterized constructors) in the other peer, association only permits function prototypes of the called functions in the class specification.
this
points to a project object that is passed to and saved by the contractor constructor, completing the association.this
points to a contractor object that is passed to and saved by the project constructor, completing the association.