๐ŸŽจ
Chapter 6

Classes and Objects

Deep dive into constructors, destructors, operator overloading, inheritance, and templates.

6.1 Constructors and Destructors

Special methods for object initialization and cleanup.

Aspect Definition Example
Constructor Special method invoked when object is created. class Tesla { Tesla() { ... } };
Destructor Special method invoked when object is destroyed. ~Tesla() { ... }
Default Constructor No arguments, initializes default values. Tesla() { battery = 0; }
Parameterized Constructor Accepts arguments to initialize data. Tesla(int b) { battery = b; }
Destructor Use Case Frees resources, cleans up memory. Called when object goes out of scope
โ–ถ Constructors and Destructors Example
#include <iostream>
using namespace std;

class Tesla {
private:
    string model;
    int batteryLevel;
    int* data;  // Dynamic memory
    
public:
    // Default constructor
    Tesla() {
        model = "Unknown";
        batteryLevel = 0;
        data = nullptr;
        cout << "Default constructor called" << endl;
    }
    
    // Parameterized constructor
    Tesla(string m, int battery) {
        model = m;
        batteryLevel = battery;
        data = new int[10];  // Allocate memory
        cout << "Parameterized constructor called for " << model << endl;
    }
    
    // Copy constructor
    Tesla(const Tesla& other) {
        model = other.model;
        batteryLevel = other.batteryLevel;
        data = new int[10];
        cout << "Copy constructor called for " << model << endl;
    }
    
    // Destructor
    ~Tesla() {
        if (data != nullptr) {
            delete[] data;  // Free memory
        }
        cout << "Destructor called for " << model << endl;
    }
    
    void display() {
        cout << "Model: " << model << ", Battery: " << batteryLevel << "%" << endl;
    }
};

int main() {
    cout << "Creating objects:" << endl;
    
    Tesla car1;  // Default constructor
    car1.display();
    
    Tesla car2("Model 3", 85);  // Parameterized constructor
    car2.display();
    
    Tesla car3 = car2;  // Copy constructor
    car3.display();
    
    cout << endl << "Objects going out of scope..." << endl;
    return 0;
    // Destructors called automatically
}
Click "Run Code" to execute

6.2 Operator Overloading

Redefining the behavior of operators for user-defined types.

Aspect Definition Example
Operator Overloading Redefine operator behavior for custom types. Tesla operator+(Tesla t1, Tesla t2);
Syntax Defined using operator keyword. Tesla operator+(Tesla t);
Overloading + Adds two objects. Tesla t3 = t1 + t2;
Overloading == Compares two objects. bool operator==(Tesla t1, Tesla t2);
Use Case Simplifies object manipulation. Mathematical operations on custom objects
โ–ถ Operator Overloading Example
#include <iostream>
using namespace std;

class Complex {
private:
    float real, imag;
    
public:
    Complex() : real(0), imag(0) {}
    Complex(float r, float i) : real(r), imag(i) {}
    
    // Overload + operator
    Complex operator+(const Complex& c) {
        Complex temp;
        temp.real = real + c.real;
        temp.imag = imag + c.imag;
        return temp;
    }
    
    // Overload - operator
    Complex operator-(const Complex& c) {
        Complex temp;
        temp.real = real - c.real;
        temp.imag = imag - c.imag;
        return temp;
    }
    
    // Overload == operator
    bool operator==(const Complex& c) {
        return (real == c.real && imag == c.imag);
    }
    
    // Overload << operator for output
    friend ostream& operator<<(ostream& out, const Complex& c) {
        out << c.real;
        if (c.imag >= 0)
            out << " + " << c.imag << "i";
        else
            out << " - " << (-c.imag) << "i";
        return out;
    }
    
    // Overload >> operator for input
    friend istream& operator>>(istream& in, Complex& c) {
        cout << "Enter real part: ";
        in >> c.real;
        cout << "Enter imaginary part: ";
        in >> c.imag;
        return in;
    }
};

int main() {
    Complex c1(3.5, 2.5);
    Complex c2(1.5, 4.5);
    
    cout << "c1 = " << c1 << endl;
    cout << "c2 = " << c2 << endl;
    
    Complex c3 = c1 + c2;
    cout << "c1 + c2 = " << c3 << endl;
    
    Complex c4 = c1 - c2;
    cout << "c1 - c2 = " << c4 << endl;
    
    if (c1 == c2) {
        cout << "c1 and c2 are equal" << endl;
    } else {
        cout << "c1 and c2 are not equal" << endl;
    }
    
    return 0;
}
Click "Run Code" to execute
โ–ถ Tesla Operator Overloading Example
#include <iostream>
using namespace std;

class Tesla {
private:
    int batteryLevel;
    
public:
    Tesla(int battery = 0) : batteryLevel(battery) {}
    
    // Overload + to combine battery levels
    Tesla operator+(const Tesla& t) {
        return Tesla(this->batteryLevel + t.batteryLevel);
    }
    
    // Overload ++ (prefix)
    Tesla& operator++() {
        batteryLevel += 10;
        if (batteryLevel > 100) batteryLevel = 100;
        return *this;
    }
    
    // Overload ++ (postfix)
    Tesla operator++(int) {
        Tesla temp = *this;
        batteryLevel += 10;
        if (batteryLevel > 100) batteryLevel = 100;
        return temp;
    }
    
    void display() {
        cout << "Battery: " << batteryLevel << "%" << endl;
    }
};

int main() {
    Tesla car1(50);
    Tesla car2(30);
    
    cout << "Car 1: ";
    car1.display();
    cout << "Car 2: ";
    car2.display();
    
    Tesla car3 = car1 + car2;
    cout << "Combined battery: ";
    car3.display();
    
    ++car1;
    cout << "After ++car1: ";
    car1.display();
    
    car2++;
    cout << "After car2++: ";
    car2.display();
    
    return 0;
}
Click "Run Code" to execute

6.3 Inheritance

Mechanism to derive new classes from existing classes.

Aspect Definition Example
Inheritance Derive new class from existing class. class EV : public Car { ... };
Single Inheritance One derived class from one base class. class Sedan : public Car { ... };
Multiple Inheritance One derived class from multiple base classes. class TeslaEV : public Car, public Engine { ... };
Hierarchical Inheritance Multiple derived classes from one base class. Truck, SUV from Car
Use Case Code reusability, method overriding. Common base class logic shared
โ–ถ Inheritance Example
#include <iostream>
using namespace std;

// Base class
class Vehicle {
protected:
    string brand;
    int year;
    
public:
    Vehicle(string b, int y) : brand(b), year(y) {
        cout << "Vehicle constructor" << endl;
    }
    
    void displayInfo() {
        cout << "Brand: " << brand << endl;
        cout << "Year: " << year << endl;
    }
    
    virtual ~Vehicle() {
        cout << "Vehicle destructor" << endl;
    }
};

// Single Inheritance
class Car : public Vehicle {
protected:
    int doors;
    
public:
    Car(string b, int y, int d) : Vehicle(b, y), doors(d) {
        cout << "Car constructor" << endl;
    }
    
    void displayCarInfo() {
        displayInfo();
        cout << "Doors: " << doors << endl;
    }
    
    ~Car() {
        cout << "Car destructor" << endl;
    }
};

// Multilevel Inheritance
class ElectricCar : public Car {
private:
    int batteryCapacity;
    
public:
    ElectricCar(string b, int y, int d, int battery) 
        : Car(b, y, d), batteryCapacity(battery) {
        cout << "ElectricCar constructor" << endl;
    }
    
    void displayFullInfo() {
        displayCarInfo();
        cout << "Battery: " << batteryCapacity << " kWh" << endl;
    }
    
    ~ElectricCar() {
        cout << "ElectricCar destructor" << endl;
    }
};

int main() {
    cout << "Creating ElectricCar object:" << endl;
    ElectricCar tesla("Tesla", 2024, 4, 100);
    
    cout << endl << "Displaying information:" << endl;
    tesla.displayFullInfo();
    
    cout << endl << "Object going out of scope:" << endl;
    return 0;
}
Click "Run Code" to execute

6.4 Templates

Create generic functions and classes that work with any data type.

Aspect Definition Example
Templates Create generic functions or classes. template <typename T>
Function Templates Generic function for multiple types. T add(T a, T b) { return a + b; }
Class Templates Generic class for multiple types. template <typename T> class Tesla { ... };
Template Specialization Customize templates for specific types. template<> class Tesla<string> { ... };
Use Case Code reusability, type safety. Generic containers like vector, list
โ–ถ Function Templates Example
#include <iostream>
using namespace std;

// Function template
template <typename T>
T getMax(T a, T b) {
    return (a > b) ? a : b;
}

// Function template with multiple parameters
template <typename T>
void swapValues(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

// Overloaded function template
template <typename T>
T add(T a, T b) {
    return a + b;
}

template <typename T>
T add(T a, T b, T c) {
    return a + b + c;
}

int main() {
    // Using template with int
    cout << "Max of 10 and 20: " << getMax(10, 20) << endl;
    
    // Using template with double
    cout << "Max of 3.5 and 2.8: " << getMax(3.5, 2.8) << endl;
    
    // Using template with char
    cout << "Max of 'a' and 'z': " << getMax('a', 'z') << endl;
    
    // Swap function
    int x = 5, y = 10;
    cout << endl << "Before swap: x = " << x << ", y = " << y << endl;
    swapValues(x, y);
    cout << "After swap: x = " << x << ", y = " << y << endl;
    
    // Add function
    cout << endl << "Add two integers: " << add(5, 10) << endl;
    cout << "Add three integers: " << add(5, 10, 15) << endl;
    cout << "Add two doubles: " << add(3.5, 2.5) << endl;
    
    return 0;
}
Click "Run Code" to execute
โ–ถ Class Templates Example
#include <iostream>
using namespace std;

// Class template
template <typename T>
class Array {
private:
    T* arr;
    int size;
    
public:
    Array(int s) {
        size = s;
        arr = new T[size];
    }
    
    ~Array() {
        delete[] arr;
    }
    
    void setElement(int index, T value) {
        if (index >= 0 && index < size) {
            arr[index] = value;
        }
    }
    
    T getElement(int index) {
        if (index >= 0 && index < size) {
            return arr[index];
        }
        return T();
    }
    
    void display() {
        cout << "Array elements: ";
        for (int i = 0; i < size; i++) {
            cout << arr[i] << " ";
        }
        cout << endl;
    }
};

// Template class for a pair
template <typename T1, typename T2>
class Pair {
private:
    T1 first;
    T2 second;
    
public:
    Pair(T1 f, T2 s) : first(f), second(s) {}
    
    void display() {
        cout << "(" << first << ", " << second << ")" << endl;
    }
    
    T1 getFirst() { return first; }
    T2 getSecond() { return second; }
};

int main() {
    // Array of integers
    Array<int> intArray(5);
    for (int i = 0; i < 5; i++) {
        intArray.setElement(i, (i + 1) * 10);
    }
    intArray.display();
    
    // Array of doubles
    Array<double> doubleArray(3);
    doubleArray.setElement(0, 3.14);
    doubleArray.setElement(1, 2.71);
    doubleArray.setElement(2, 1.41);
    doubleArray.display();
    
    // Pair examples
    Pair<int, int> p1(10, 20);
    cout << endl << "Integer pair: ";
    p1.display();
    
    Pair<string, int> p2("Tesla", 369);
    cout << "String-Int pair: ";
    p2.display();
    
    Pair<double, string> p3(3.14, "Pi");
    cout << "Double-String pair: ";
    p3.display();
    
    return 0;
}
Click "Run Code" to execute

๐Ÿ’ก Practice Exercise

Master classes and objects with these exercises:

  • Create a class with constructor, destructor, and copy constructor
  • Overload arithmetic operators for a custom class
  • Implement a class hierarchy with inheritance
  • Create a template function that works with different data types
  • Build a template class for a stack data structure