Association tells about the relationship of one class with another. It generally implies "HAS-A" relationship between two classes. For example, consider two classes:
Car
Driver
then, there is an association between these two classes because Car has a Driver.
Consider another example:
Car
Engine
then, there is an association between these two classes because Car has an engine.
Association is further divided into two sub parts depending upon the type of bonds:
Aggregation
Composition
Aggregation implies a weak bond between two classes i.e. both the classes can exist independently. For example, classes Car and Driver can exist independently. There is no lifecycle dependency of classes on each other.
Composition implies a strong bond between two classes i.e. if one class is destroyed then other class is also destroyed. For example, if class Engine is destroyed then there is no existence of class Car. If there is no engine then alone car is of no use. There is a lifecycle dependency of one class on another.
Let us understand both concepts through code
-Aggregation
#include <iostream>
#include <string>
using namespace std;
class Driver {
string name;
int age;
public:
// Constructor
Driver(string name, int age) {
this->name = name;
this->age = age;
}
void printDriverInfo() {
cout << "Name of Driver - " << name << endl;
cout << "Age - " << age << endl;
}
// Destructor
~Driver() { cout << "Driver object destroyed" << endl; }
};
class Car {
string name;
int noOfWheels;
Driver *driver;
public:
// Constructor
Car(string name, int noOfWheels, Driver *driver) {
this->name = name;
this->noOfWheels = noOfWheels;
this->driver = driver;
}
void printCarInfo() {
cout << "Name of Car - " << name << endl;
cout << "No of wheels - " << noOfWheels << endl;
cout << "\n";
driver->printDriverInfo();
cout << "\n";
}
// Destructor
~Car() { cout << "Car object destroyed" << endl; }
};
int main() {
// initializing Driver object
Driver *driver = new Driver("Rahul", 26);
/* Created the Car object inside a block so that it
is destroyed as soon as the block exits */
{
Car car("Verna", 4, driver);
car.printCarInfo();
} // Car object destroyed here
// Driver object still exists even after Car object is destroyed
cout << "\nDriver object still exists -\n";
driver->printDriverInfo();
return 0;
}
In the above example, when the Car object is destroyed, the Driver object still exists and we can access the Driver class member variables.
OUTPUT-
-Composition
#include <iostream>
#include <string>
using namespace std;
class Engine {
string type;
int id;
public:
// Constructor
Engine(string type, int id) {
this->type = type;
this->id = id;
}
void printEngineInfo() {
cout << "Engine Type - " << type << endl;
cout << "Engine ID - " << id << endl;
}
// Destructor
~Engine() { cout << "Engine object destroyed" << endl; }
};
class Car {
string name;
int noOfWheels;
// Engine object is created as a member variable
Engine engine;
public:
// Constructor
Car(string name, int noOfWheels) : engine("Diesel", 142) {
this->name = name;
this->noOfWheels = noOfWheels;
}
void printCarInfo() {
cout << "Name of Car - " << name << endl;
cout << "No of wheels - " << noOfWheels << endl;
cout << "\n";
cout << "Engine Info - " << endl;
// can access Engine member variables from within Car class only
engine.printEngineInfo();
}
// Destructor
~Car() { cout << "Car object destroyed" << endl; }
};
int main() {
/* Created the Car object inside a block so that it
is destroyed as soon as the block exits */
{
Car car("Verna", 4);
car.printCarInfo();
cout << "\n";
} // Both Car and Engine objects are destroyed simultaneously
// printing a message to see if both Engine and Car objects are destroyed
// before this
cout << "Both objects destroyed" << endl;
return 0;
}
In the above example, when the Car object is destroyed, the Engine object is also destroyed which depicts that when the engine is no more the car is of no use.
OUTPUT-