To qualify as an association, an object and another object must have the following relationship:
The associated object (member) is otherwise unrelated to the object (class)
The associated object (member) can belong to more than one object (class) at a time
The associated object (member) does not have its existence managed by the object (class)
The associated object (member) may or may not know about the existence of the object (class)
Unlike an Aggregation, where the relationship is always Unidirectional,
in an Association, the relationship may beUnidirectional or Bidirectional (where the two objects are aware of each other).
Association models as uses-a relationship.
Most often, Associations are implemented using POINTERS, where the object points at the associated object.
In general, you should avoid bidirectional associations if a unidirectional one will do, as they add complexity and tend to be harder to write without making errors.
#include<iostream>
#include<string>
#include<vector>// Since these classes have a circular dependency, we're going to forward declare DoctorclassDoctor;classPatient{private:std::stringm_name;std::vector<Doctor*>m_doctor;// so that we can use it here// We're going to make addDoctor private because we don't want the public to use it.// They should use Doctor::addPatient() instead, which is publicly exposed// We'll define this function after we define what a Doctor is// Since we need Doctor to be defined in order to actually use anything from itvoidaddDoctor(Doctor*doc);public:Patient(std::stringname):m_name(name){}// We'll implement this function below Doctor since we need Doctor to be defined at that pointfriendstd::ostream&operator<<(std::ostream&out,constPatient&pat);std::stringgetName()const{returnm_name;}// We're friending Doctor so that class can access the private addDoctor() function// (Note: in normal circumstances, we'd just friend that one function, but we can't// because Doctor is forward declared)friendclassDoctor;};classDoctor{private:std::stringm_name;std::vector<Patient*>m_patient;public:Doctor(std::stringname):m_name(name){}voidaddPatient(Patient*pat){// Our doctor will add this patientm_patient.push_back(pat);// and the patient will also add this doctorpat->addDoctor(this);}friendstd::ostream&operator<<(std::ostream&out,constDoctor&doc){unsignedintlength=doc.m_patient.size();if(length==0){out<<doc.m_name<<" has no patients right now";returnout;}out<<doc.m_name<<" is seeing patients: ";for(unsignedintcount=0;count<length;++count)out<<doc.m_patient[count]->getName()<<' ';returnout;}std::stringgetName()const{returnm_name;}};voidPatient::addDoctor(Doctor*doc){m_doctor.push_back(doc);}std::ostream&operator<<(std::ostream&out,constPatient&pat){unsignedintlength=pat.m_doctor.size();if(length==0){out<<pat.getName()<<" has no doctors right now";returnout;}out<<pat.m_name<<" is seeing doctors: ";for(unsignedintcount=0;count<length;++count)out<<pat.m_doctor[count]->getName()<<' ';returnout;}intmain(){// Create a Patient outside the scope of the DoctorPatient*p1=newPatient("Dave");Patient*p2=newPatient("Frank");Patient*p3=newPatient("Betsy");Doctor*d1=newDoctor("James");Doctor*d2=newDoctor("Scott");d1->addPatient(p1);d2->addPatient(p1);d2->addPatient(p3);std::cout<<*d1<<'\n';std::cout<<*d2<<'\n';std::cout<<*p1<<'\n';std::cout<<*p2<<'\n';std::cout<<*p3<<'\n';deletep1;deletep2;deletep3;deleted1;deleted2;return0;}
Reflexive Association
Sometimes objects may have a relationship with other objects of the same type. This is called a Reflexive Association.
A good example of a Reflexive Association is the relationship between a university course and its prerequisites (which are also university courses).
This can lead to a chain of Associations (a course has a prerequisite, which has a prerequisite, etc,..)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<string>classCourse{private:std::stringm_name;// 'Course' class itself has a 'Course' member --> Reflexive AssociationCourse*m_prerequisite;public:Course(std::string&name,Course*prerequisite=nullptr):m_name(name),m_prerequisite(prerequisite){}};
Indirect Association
In an Association, A pointer is not strictly required to directly link objects together.
Any kind of data that allows you to link two objects together suffices.
In the following example, we show how a Driver class can have a unidirectional association with a Car without actually including a Car pointer member.
we have a CarLot holding our cars.
The Driver, who needs a car, doesn’t have a pointer to his Car, instead, he has the ID of the car, which we can use to get the Car from the CarLot when we need it.
#include<iostream>
#include<string>classCar{private:std::stringm_name;intm_id;public:Car(std::stringname,intid):m_name(name),m_id(id){}std::stringgetName(){returnm_name;}intgetId(){returnm_id;}};// Our CarLot is essentially just a static array of Cars and a lookup function to retrieve them.// Because it's static, we don't need to allocate an object of type CarLot to use itclassCarLot{private:staticCars_carLot[4];public:// CarLot() = delete; // Ensure we don't try to allocate a CarLot// C++ 11 feature, hence here using simple constructorCarLot();staticCar*getCar(intid){for(intcount=0;count<4;++count)if(s_carLot[count].getId()==id)return&(s_carLot[count]);returnnullptr;}};CarCarLot::s_carLot[4]={Car("Prius",4),Car("Corolla",17),Car("Accord",84),Car("Matrix",62)};classDriver{private:std::stringm_name;intm_carId;// we're associated with the Car by ID rather than pointerpublic:Driver(std::stringname,intcarId):m_name(name),m_carId(carId){}std::stringgetName(){returnm_name;}intgetCarId(){returnm_carId;}};intmain(){Driverd("Franz",17);// Franz is driving the car with ID 17Car*car=CarLot::getCar(d.getCarId());// Get that car from the car lotif(car)std::cout<<d.getName()<<" is driving a "<<car->getName()<<'\n';elsestd::cout<<d.getName()<<" couldn't find his car\n";return0;}