cpp
relationships
01_relationships.cpp⚙️cpp
/**
* ============================================================
* C++ OBJECT RELATIONSHIPS
* ============================================================
*
* This file covers:
* - Association (uses-a)
* - Aggregation (has-a, weak)
* - Composition (has-a, strong)
* - Dependency (depends-on)
* - Comparison of relationships
*
* Compile: g++ -std=c++17 -Wall 01_relationships.cpp -o relationships
* Run: ./relationships
*
* ============================================================
*/
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <algorithm> // ⚠️ LEARNING NOTE: This was missing!
// std::find() is in <algorithm>, not automatically available.
// Common STL algorithms need this header:
// find, sort, count, copy, transform, etc.
using namespace std;
// ============================================================
// PART 1: ASSOCIATION (USES-A)
// ============================================================
// - Objects are independent
// - Can exist without each other
// - Usually implemented via pointer/reference
class Course; // Forward declaration
class Student {
private:
string name;
int id;
vector<Course*> enrolledCourses; // Association - doesn't own courses
public:
Student(const string& n, int i) : name(n), id(i) {}
void enroll(Course* course) {
enrolledCourses.push_back(course);
}
void unenroll(Course* course) {
auto it = find(enrolledCourses.begin(), enrolledCourses.end(), course);
if (it != enrolledCourses.end()) {
enrolledCourses.erase(it);
}
}
string getName() const { return name; }
int getId() const { return id; }
size_t getCourseCount() const { return enrolledCourses.size(); }
void listCourses() const; // Defined after Course
};
class Course {
private:
string code;
string title;
vector<Student*> students; // Association - doesn't own students
public:
Course(const string& c, const string& t) : code(c), title(t) {}
void addStudent(Student* student) {
students.push_back(student);
}
void removeStudent(Student* student) {
auto it = find(students.begin(), students.end(), student);
if (it != students.end()) {
students.erase(it);
}
}
string getCode() const { return code; }
string getTitle() const { return title; }
size_t getStudentCount() const { return students.size(); }
void listStudents() const {
cout << "Course " << code << ": " << title << endl;
cout << "Students enrolled: " << students.size() << endl;
for (const auto* s : students) {
cout << " - " << s->getName() << " (" << s->getId() << ")" << endl;
}
}
};
void Student::listCourses() const {
cout << "Student: " << name << " (ID: " << id << ")" << endl;
cout << "Enrolled in " << enrolledCourses.size() << " courses:" << endl;
for (const auto* c : enrolledCourses) {
cout << " - " << c->getCode() << ": " << c->getTitle() << endl;
}
}
// ============================================================
// PART 2: AGGREGATION (HAS-A, WEAK OWNERSHIP)
// ============================================================
// - Container has parts but doesn't own them
// - Parts can exist independently
// - Parts can be shared between containers
class Employee {
private:
string name;
int id;
public:
Employee(const string& n, int i) : name(n), id(i) {
cout << " Employee created: " << name << endl;
}
~Employee() {
cout << " Employee destroyed: " << name << endl;
}
string getName() const { return name; }
int getId() const { return id; }
};
class Department {
private:
string name;
vector<Employee*> employees; // Aggregation - doesn't own employees
public:
Department(const string& n) : name(n) {
cout << "Department created: " << name << endl;
}
~Department() {
cout << "Department destroyed: " << name << endl;
// Does NOT delete employees - they exist independently
}
void addEmployee(Employee* emp) {
employees.push_back(emp);
cout << " Added " << emp->getName() << " to " << name << endl;
}
void removeEmployee(Employee* emp) {
auto it = find(employees.begin(), employees.end(), emp);
if (it != employees.end()) {
employees.erase(it);
cout << " Removed " << emp->getName() << " from " << name << endl;
}
}
void listEmployees() const {
cout << "Department: " << name << endl;
for (const auto* e : employees) {
cout << " - " << e->getName() << " (" << e->getId() << ")" << endl;
}
}
string getName_() const { return name; }
};
// ============================================================
// PART 3: COMPOSITION (HAS-A, STRONG OWNERSHIP)
// ============================================================
// - Container owns its parts
// - Parts cannot exist independently
// - Parts destroyed when container destroyed
class Engine {
private:
string type;
int horsepower;
public:
Engine(const string& t, int hp) : type(t), horsepower(hp) {
cout << " Engine created: " << type << ", " << hp << "hp" << endl;
}
~Engine() {
cout << " Engine destroyed: " << type << endl;
}
void start() const { cout << "Engine starting: " << type << endl; }
void stop() const { cout << "Engine stopping: " << type << endl; }
string getType() const { return type; }
int getHorsepower() const { return horsepower; }
};
class Wheel {
private:
string brand;
int size;
public:
Wheel(const string& b, int s) : brand(b), size(s) {
cout << " Wheel created: " << brand << " " << s << "\"" << endl;
}
~Wheel() {
cout << " Wheel destroyed: " << brand << endl;
}
void rotate() const { cout << "Wheel rotating" << endl; }
string getBrand() const { return brand; }
};
class Car {
private:
string model;
Engine engine; // Composition - owns the engine
vector<Wheel> wheels; // Composition - owns the wheels
public:
Car(const string& m, const string& engineType, int hp,
const string& wheelBrand, int wheelSize)
: model(m), engine(engineType, hp) {
cout << " Creating Car: " << model << endl;
// Create 4 wheels
for (int i = 0; i < 4; i++) {
wheels.emplace_back(wheelBrand, wheelSize);
}
}
~Car() {
cout << " Destroying Car: " << model << endl;
// Engine and wheels automatically destroyed
}
void start() {
cout << model << " starting..." << endl;
engine.start();
}
void stop() {
cout << model << " stopping..." << endl;
engine.stop();
}
void displayInfo() const {
cout << "Car: " << model << endl;
cout << " Engine: " << engine.getType()
<< " (" << engine.getHorsepower() << "hp)" << endl;
cout << " Wheels: " << wheels.size() << " x "
<< wheels[0].getBrand() << endl;
}
};
// ============================================================
// PART 4: COMPOSITION WITH SMART POINTERS
// ============================================================
class Room {
private:
string name;
int area;
public:
Room(const string& n, int a) : name(n), area(a) {
cout << " Room created: " << name << " (" << area << " sqft)" << endl;
}
~Room() {
cout << " Room destroyed: " << name << endl;
}
string getName() const { return name; }
int getArea() const { return area; }
};
class House {
private:
string address;
vector<unique_ptr<Room>> rooms; // Composition with smart pointers
public:
House(const string& addr) : address(addr) {
cout << " House created: " << address << endl;
}
~House() {
cout << " House destroyed: " << address << endl;
// Rooms automatically destroyed by unique_ptr
}
void addRoom(const string& name, int area) {
rooms.push_back(make_unique<Room>(name, area));
}
void displayInfo() const {
cout << "House at: " << address << endl;
int totalArea = 0;
for (const auto& room : rooms) {
cout << " - " << room->getName() << ": " << room->getArea() << " sqft" << endl;
totalArea += room->getArea();
}
cout << " Total area: " << totalArea << " sqft" << endl;
}
};
// ============================================================
// PART 5: DEPENDENCY (DEPENDS-ON)
// ============================================================
// - Weakest relationship
// - Object uses another temporarily
// - Usually as function parameter
class Logger {
public:
static void log(const string& message) {
cout << "[LOG] " << message << endl;
}
};
class Database {
public:
bool connect(const string& connectionString) {
Logger::log("Connecting to: " + connectionString); // Dependency on Logger
return true;
}
void query(const string& sql) {
Logger::log("Executing: " + sql); // Dependency on Logger
}
};
class Formatter {
public:
static string formatCurrency(double amount) {
return "$" + to_string(amount);
}
};
class Invoice {
public:
void print(double amount) {
// Dependency on Formatter - uses it but doesn't store
cout << "Amount due: " << Formatter::formatCurrency(amount) << endl;
}
};
// ============================================================
// MAIN FUNCTION
// ============================================================
int main() {
cout << "============================================" << endl;
cout << " C++ OBJECT RELATIONSHIPS" << endl;
cout << "============================================" << endl << endl;
// ========================================================
// DEMO 1: Association
// ========================================================
cout << "--- DEMO 1: ASSOCIATION ---" << endl << endl;
Student alice("Alice", 1001);
Student bob("Bob", 1002);
Course cpp("CS101", "C++ Programming");
Course db("CS201", "Database Systems");
// Establish associations
alice.enroll(&cpp);
alice.enroll(&db);
bob.enroll(&cpp);
cpp.addStudent(&alice);
cpp.addStudent(&bob);
db.addStudent(&alice);
alice.listCourses();
cout << endl;
cpp.listStudents();
// Both can exist independently
cout << "\n💡 Students and Courses exist independently" << endl;
cout << endl;
// ========================================================
// DEMO 2: Aggregation
// ========================================================
cout << "--- DEMO 2: AGGREGATION ---" << endl << endl;
cout << "Creating employees:" << endl;
Employee* emp1 = new Employee("John", 101);
Employee* emp2 = new Employee("Jane", 102);
Employee* emp3 = new Employee("Mike", 103);
cout << "\nCreating departments:" << endl;
{
Department engineering("Engineering");
Department marketing("Marketing");
engineering.addEmployee(emp1);
engineering.addEmployee(emp2);
marketing.addEmployee(emp2); // Jane works in both!
marketing.addEmployee(emp3);
cout << endl;
engineering.listEmployees();
cout << endl;
marketing.listEmployees();
cout << "\nDepartments going out of scope..." << endl;
}
cout << "\n💡 Employees still exist after departments are destroyed!" << endl;
cout << "emp1 name: " << emp1->getName() << endl;
// Clean up (in real code, would use smart pointers)
delete emp1;
delete emp2;
delete emp3;
cout << endl;
// ========================================================
// DEMO 3: Composition
// ========================================================
cout << "--- DEMO 3: COMPOSITION ---" << endl << endl;
cout << "Creating a car (parts created with it):" << endl;
{
Car myCar("Tesla Model 3", "Electric Motor", 450, "Michelin", 18);
cout << endl;
myCar.displayInfo();
cout << endl;
myCar.start();
myCar.stop();
cout << "\nCar going out of scope..." << endl;
}
cout << "\n💡 Engine and wheels destroyed WITH the car!" << endl;
cout << endl;
// ========================================================
// DEMO 4: Composition with Smart Pointers
// ========================================================
cout << "--- DEMO 4: SMART POINTER COMPOSITION ---" << endl << endl;
cout << "Creating a house:" << endl;
{
House myHouse("123 Main Street");
myHouse.addRoom("Living Room", 400);
myHouse.addRoom("Kitchen", 200);
myHouse.addRoom("Bedroom", 250);
myHouse.addRoom("Bathroom", 100);
cout << endl;
myHouse.displayInfo();
cout << "\nHouse going out of scope..." << endl;
}
cout << "\n💡 Rooms destroyed automatically with house!" << endl;
cout << endl;
// ========================================================
// DEMO 5: Dependency
// ========================================================
cout << "--- DEMO 5: DEPENDENCY ---" << endl << endl;
Database db2;
db2.connect("localhost:5432/mydb");
db2.query("SELECT * FROM users");
Invoice invoice;
invoice.print(199.99);
cout << "\n💡 Database uses Logger, Invoice uses Formatter" << endl;
cout << " but neither stores or owns them!" << endl;
cout << endl;
// ========================================================
// RELATIONSHIP COMPARISON
// ========================================================
cout << "--- RELATIONSHIP COMPARISON ---" << endl << endl;
cout << "┌────────────┬───────────────────────────────────────────┐" << endl;
cout << "│ Relation │ Description │" << endl;
cout << "├────────────┼───────────────────────────────────────────┤" << endl;
cout << "│ Dependency │ Uses temporarily (function parameter) │" << endl;
cout << "│ Association│ Uses (pointer/ref, no ownership) │" << endl;
cout << "│ Aggregation│ Has-a, weak (parts can exist alone) │" << endl;
cout << "│ Composition│ Has-a, strong (parts die with whole) │" << endl;
cout << "│ Inheritance│ Is-a (derived from base) │" << endl;
cout << "└────────────┴───────────────────────────────────────────┘" << endl;
cout << "\nImplementation Patterns:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "• Dependency: Parameter, local variable" << endl;
cout << "• Association: Raw pointer, reference" << endl;
cout << "• Aggregation: shared_ptr, raw pointer" << endl;
cout << "• Composition: Value, unique_ptr" << endl;
cout << endl;
cout << "============================================" << endl;
cout << "OBJECT RELATIONSHIPS COMPLETE!" << endl;
cout << "============================================" << endl;
return 0;
}
// ============================================================
// EXERCISES:
// ============================================================
/*
* 1. Design a Library System:
* - Association: Library ↔ Member
* - Aggregation: Library → Book (books can exist without library)
* - Composition: Book → Chapter (chapters die with book)
*
* 2. Design a Computer:
* - Composition: Computer → CPU, RAM, Motherboard
* - Aggregation: Computer → Peripherals (keyboard, mouse)
* - Show proper lifetime management
*
* 3. Design a Music Playlist:
* - Association: Playlist ↔ Song
* - A song can be in multiple playlists
* - Deleting playlist doesn't delete songs
*
* 4. Design an Order System:
* - Composition: Order → OrderItem
* - Association: Order ↔ Customer
* - Dependency: Order uses PaymentProcessor
*/