Unit 3.0: Functions, Classes & Data Abstraction 🧩

1. Parts of a Function

  • A function is a self-contained block of code that performs a specific task. Every function has well-defined parts.
┌─────────────────────────────────────────┐
│  return_type function_name(parameters)  │  ← Function Header
│  {                                      │
│      // local variables                 │  ← Function Body
│      // statements                      │
│      return value;                      │  ← Return Statement
│  }                                      │
└─────────────────────────────────────────┘

Anatomy of a Function

#include <iostream>
using namespace std;
 
//  ↓ return type   ↓ name    ↓ parameters
    int             add      (int a, int b)
{
    // body
    int result = a + b;  // local variable
    return result;       // return statement
}
 
int main() {
    // Function call
    int sum = add(3, 4);   // 3 and 4 are arguments
    cout << sum << endl;   // 7
    return 0;
}

Five Parts of a Function

PartDescriptionExample
Return typeType of value function returnsint, double, void
Function nameIdentifier for the functionadd, display
Parameter listInput values (can be empty)(int a, int b)
Function bodyStatements inside {}{ return a+b; }
Return statementSends value back to callerreturn result;

Function Declaration (Prototype)

#include <iostream>
using namespace std;
 
// Function prototype / declaration (before main)
int multiply(int a, int b);   // tells compiler about the function
 
int main() {
    cout << multiply(4, 5) << endl;  // 20
    return 0;
}
 
// Function definition (can be after main)
int multiply(int a, int b) {
    return a * b;
}

2. User-Defined Functions

Functions created by the programmer (as opposed to built-in library functions).

Types of User-Defined Functions

User-Defined Functions
├── No return value, No parameters      void display()
├── No return value, With parameters    void greet(string name)
├── With return value, No parameters    int getNumber()
└── With return value, With parameters  int add(int a, int b)

Examples of All Four Types

#include <iostream>
#include <string>
using namespace std;
 
// Type 1: No return, No parameters
void showWelcome() {
    cout << "=== Welcome to C++ ===" << endl;
}
 
// Type 2: No return, With parameters
void greet(string name) {
    cout << "Hello, " << name << "!" << endl;
}
 
// Type 3: With return, No parameters
int getRandomSeed() {
    return 42;
}
 
// Type 4: With return, With parameters
double calculateArea(double length, double width) {
    return length * width;
}
 
int main() {
    showWelcome();                       // Type 1
    greet("Alice");                      // Type 2
    int seed = getRandomSeed();          // Type 3
    double area = calculateArea(5.0, 3.0); // Type 4
 
    cout << "Seed: " << seed << endl;
    cout << "Area: " << area << endl;
    return 0;
}

Practical User-Defined Functions

#include <iostream>
#include <cmath>
using namespace std;
 
// Check if a number is prime
bool isPrime(int n) {
    if (n < 2) return false;
    for (int i = 2; i <= sqrt(n); i++) {
        if (n % i == 0) return false;
    }
    return true;
}
 
// Factorial using recursion
long long factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}
 
// Power function
double power(double base, int exp) {
    double result = 1.0;
    for (int i = 0; i < exp; i++) {
        result *= base;
    }
    return result;
}
 
int main() {
    cout << isPrime(17) << endl;        // 1 (true)
    cout << factorial(6) << endl;       // 720
    cout << power(2.0, 10) << endl;     // 1024
    return 0;
}

3. Value-Returning Functions

A function that returns a computed value to the caller using the return statement.

Single Return Value

#include <iostream>
#include <string>
using namespace std;
 
int square(int x) {
    return x * x;
}
 
double celsiusToFahrenheit(double c) {
    return (c * 9.0 / 5.0) + 32.0;
}
 
string getGrade(int marks) {
    if (marks >= 90) return "A";
    if (marks >= 80) return "B";
    if (marks >= 70) return "C";
    if (marks >= 60) return "D";
    return "F";
}
 
int findMax(int a, int b, int c) {
    if (a >= b && a >= c) return a;
    if (b >= c)           return b;
    return c;
}
 
int main() {
    cout << square(7) << endl;                  // 49
    cout << celsiusToFahrenheit(100) << endl;   // 212
    cout << getGrade(85) << endl;               // B
    cout << findMax(10, 35, 22) << endl;        // 35
    return 0;
}

Returning Multiple Values (using struct or pair)

#include <iostream>
#include <utility>    // for pair
using namespace std;
 
// Return two values using pair
pair<int, int> divmod(int a, int b) {
    return {a / b, a % b};   // quotient and remainder
}
 
// Return struct for multiple values
struct Stats {
    double min;
    double max;
    double average;
};
 
Stats analyze(int arr[], int n) {
    Stats s;
    s.min = s.max = arr[0];
    double sum = 0;
    for (int i = 0; i < n; i++) {
        if (arr[i] < s.min) s.min = arr[i];
        if (arr[i] > s.max) s.max = arr[i];
        sum += arr[i];
    }
    s.average = sum / n;
    return s;
}
 
int main() {
    auto [q, r] = divmod(17, 5);   // C++17 structured bindings
    cout << "17 / 5 = " << q << " rem " << r << endl;  // 3 rem 2
 
    int data[] = {4, 2, 8, 1, 9, 3};
    Stats result = analyze(data, 6);
    cout << "Min: " << result.min << endl;      // 1
    cout << "Max: " << result.max << endl;      // 9
    cout << "Avg: " << result.average << endl;  // 4.5
 
    return 0;
}

4. void Functions

A void function performs an action but does not return a value.

Examples

#include <iostream>
#include <string>
using namespace std;
 
// Printing / Display functions
void printDivider(char ch = '-', int width = 40) {
    for (int i = 0; i < width; i++) cout << ch;
    cout << endl;
}
 
void printTable(int n) {
    printDivider('=');
    cout << "Multiplication Table of " << n << endl;
    printDivider('-');
    for (int i = 1; i <= 10; i++) {
        cout << n << " x " << i << " = " << n * i << endl;
    }
    printDivider('=');
}
 
// Modification function (modifies via reference)
void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}
 
// Sort an array (modifies in-place)
void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr[j], arr[j + 1]);
            }
        }
    }
}
 
int main() {
    printTable(5);
 
    int a = 10, b = 20;
    swap(a, b);
    cout << "a=" << a << ", b=" << b << endl;  // a=20, b=10
 
    int arr[] = {64, 25, 12, 22, 11};
    bubbleSort(arr, 5);
    for (int x : arr) cout << x << " ";  // 11 12 22 25 64
    cout << endl;
    return 0;
}

5. Value Parameters

How arguments are passed to functions.

Pass by Value (Copy)

#include <iostream>
using namespace std;
 
void tryToModify(int x) {
    x = 999;    // changes LOCAL copy only
    cout << "Inside: " << x << endl;  // 999
}
 
int main() {
    int num = 42;
    tryToModify(num);
    cout << "Outside: " << num << endl;  // 42 (unchanged!)
    return 0;
}

Pass by Reference (Alias)

#include <iostream>
using namespace std;
 
void doubleIt(int& x) {   // & makes it a reference
    x *= 2;               // modifies the ORIGINAL
}
 
void getMinMax(int a, int b, int& minVal, int& maxVal) {
    minVal = (a < b) ? a : b;
    maxVal = (a > b) ? a : b;
}
 
int main() {
    int n = 10;
    doubleIt(n);
    cout << n << endl;   // 20 (modified!)
 
    int lo, hi;
    getMinMax(15, 7, lo, hi);
    cout << "Min: " << lo << ", Max: " << hi << endl;  // Min: 7, Max: 15
    return 0;
}

Pass by Pointer

#include <iostream>
using namespace std;
 
void increment(int* ptr) {
    (*ptr)++;    // dereference and modify
}
 
int main() {
    int val = 5;
    increment(&val);    // pass address
    cout << val << endl;  // 6
    return 0;
}

Const Parameters (Protect from modification)

#include <iostream>
#include <string>
using namespace std;
 
// const ref: efficient (no copy) + safe (no modification)
void display(const string& msg) {
    // msg = "changed";  // ERROR: msg is const
    cout << msg << endl;
}
 
double circleArea(const double radius) {
    // radius = 10;  // ERROR
    return 3.14159 * radius * radius;
}
 
int main() {
    display("Hello World");        // no string copy made
    cout << circleArea(5.0) << endl;
    return 0;
}

Default Parameters

#include <iostream>
using namespace std;
 
void greet(string name, string greeting = "Hello") {
    cout << greeting << ", " << name << "!" << endl;
}
 
double interest(double principal, double rate = 0.05, int years = 1) {
    return principal * rate * years;
}
 
int main() {
    greet("Alice");              // Hello, Alice!
    greet("Bob", "Good morning"); // Good morning, Bob!
 
    cout << interest(1000) << endl;          // 50 (5% for 1 year)
    cout << interest(1000, 0.08) << endl;    // 80 (8% for 1 year)
    cout << interest(1000, 0.08, 3) << endl; // 240 (8% for 3 years)
    return 0;
}

6. Function Overloading

Defining multiple functions with the same name but different parameter lists.

Rules for Overloading

  • Must differ in number of parameters, OR
  • Must differ in types of parameters, OR
  • Must differ in order of parameter types
  • Return type alone is NOT sufficient for overloading
#include <iostream>
#include <string>
using namespace std;
 
// Overloaded 'add' functions
int add(int a, int b) {
    cout << "int + int: ";
    return a + b;
}
 
double add(double a, double b) {
    cout << "double + double: ";
    return a + b;
}
 
int add(int a, int b, int c) {
    cout << "int + int + int: ";
    return a + b + c;
}
 
string add(string a, string b) {
    cout << "string + string: ";
    return a + b;
}
 
int main() {
    cout << add(3, 4) << endl;              // int + int: 7
    cout << add(1.5, 2.5) << endl;          // double + double: 4
    cout << add(1, 2, 3) << endl;           // int + int + int: 6
    cout << add(string("Hi"), string(" there")) << endl;  // string: Hi there
    return 0;
}

Practical Overloading Example

#include <iostream>
#include <cmath>
using namespace std;
 
// Overloaded area functions
double area(double radius) {
    return 3.14159 * radius * radius;  // circle
}
 
double area(double length, double width) {
    return length * width;             // rectangle
}
 
double area(double base, double height, bool isTriangle) {
    return 0.5 * base * height;        // triangle
}
 
int main() {
    cout << "Circle area:    " << area(5.0) << endl;
    cout << "Rectangle area: " << area(4.0, 6.0) << endl;
    cout << "Triangle area:  " << area(3.0, 8.0, true) << endl;
    return 0;
}

7. Virtual Functions

A virtual function enables runtime polymorphism — the correct function is called based on the actual object type, not the pointer type.

Without Virtual (Wrong behavior)

#include <iostream>
using namespace std;
 
class Animal {
public:
    void sound() {   // NOT virtual
        cout << "Some sound" << endl;
    }
};
 
class Dog : public Animal {
public:
    void sound() {
        cout << "Woof!" << endl;
    }
};
 
int main() {
    Animal* ptr = new Dog();
    ptr->sound();   // "Some sound" ← WRONG! Calls Animal's version
    delete ptr;
    return 0;
}

With Virtual (Correct behavior)

#include <iostream>
using namespace std;
 
class Animal {
public:
    virtual void sound() {    // virtual keyword
        cout << "Some sound" << endl;
    }
    virtual ~Animal() {}      // Always make destructor virtual in base class
};
 
class Dog : public Animal {
public:
    void sound() override {   // override keyword (C++11, recommended)
        cout << "Woof!" << endl;
    }
};
 
class Cat : public Animal {
public:
    void sound() override {
        cout << "Meow!" << endl;
    }
};
 
class Duck : public Animal {
public:
    void sound() override {
        cout << "Quack!" << endl;
    }
};
 
void makeSound(Animal* animal) {
    animal->sound();    // calls correct derived class function
}
 
int main() {
    Animal* animals[] = {new Dog(), new Cat(), new Duck()};
 
    for (Animal* a : animals) {
        makeSound(a);   // Woof! / Meow! / Quack!
    }
 
    for (Animal* a : animals) delete a;
    return 0;
}

Pure Virtual Functions (Abstract Class)

#include <iostream>
using namespace std;
 
class Shape {
public:
    virtual double area() = 0;       // pure virtual: = 0
    virtual double perimeter() = 0;  // pure virtual
    virtual void display() {
        cout << "Area: " << area() << ", Perimeter: " << perimeter() << endl;
    }
    virtual ~Shape() {}
};
// Shape is now an ABSTRACT CLASS — cannot be instantiated directly
 
class Circle : public Shape {
    double radius;
public:
    Circle(double r) : radius(r) {}
    double area() override      { return 3.14159 * radius * radius; }
    double perimeter() override { return 2 * 3.14159 * radius; }
};
 
class Rectangle : public Shape {
    double l, w;
public:
    Rectangle(double l, double w) : l(l), w(w) {}
    double area() override      { return l * w; }
    double perimeter() override { return 2 * (l + w); }
};
 
int main() {
    // Shape s;  // ERROR: cannot instantiate abstract class
    Shape* s1 = new Circle(5.0);
    Shape* s2 = new Rectangle(4.0, 6.0);
 
    s1->display();  // Area: 78.54, Perimeter: 31.42
    s2->display();  // Area: 24, Perimeter: 20
 
    delete s1;
    delete s2;
    return 0;
}

8. Structure in C++

A struct groups related variables of different types under one name.

Basic Structure

#include <iostream>
#include <string>
using namespace std;
 
struct Student {
    string name;
    int rollNo;
    float marks;
    char grade;
};
 
int main() {
    // Declare and initialize
    Student s1 = {"Alice", 101, 88.5f, 'B'};
    Student s2;
    s2.name = "Bob";
    s2.rollNo = 102;
    s2.marks = 95.0f;
    s2.grade = 'A';
 
    cout << s1.name << " | " << s1.rollNo << " | " << s1.marks << endl;
    cout << s2.name << " | " << s2.rollNo << " | " << s2.marks << endl;
    return 0;
}

Struct with Functions

#include <iostream>
#include <string>
#include <cmath>
using namespace std;
 
struct Point {
    double x;
    double y;
 
    // Member function inside struct
    double distanceTo(const Point& other) const {
        double dx = x - other.x;
        double dy = y - other.y;
        return sqrt(dx*dx + dy*dy);
    }
 
    void display() const {
        cout << "(" << x << ", " << y << ")" << endl;
    }
};
 
int main() {
    Point p1 = {0.0, 0.0};
    Point p2 = {3.0, 4.0};
 
    p1.display();
    p2.display();
    cout << "Distance: " << p1.distanceTo(p2) << endl;  // 5
    return 0;
}

struct vs class (Key Difference)

struct MyStruct {
    int x;       // public by default
    void show() { cout << x; }
};
 
class MyClass {
    int x;       // private by default
public:
    void show() { cout << x; }
};

9. Class

A class is a user-defined blueprint that bundles data (attributes) and functions (methods) together.

Class Definition and Object Creation

#include <iostream>
#include <string>
using namespace std;
 
class BankAccount {
private:
    string owner;
    double balance;
    int accountNo;
 
public:
    // Constructor
    BankAccount(string name, int accNo, double initialBalance = 0.0) {
        owner = name;
        accountNo = accNo;
        balance = initialBalance;
    }
 
    // Methods
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            cout << "Deposited: " << amount << endl;
        }
    }
 
    void withdraw(double amount) {
        if (amount > balance) {
            cout << "Insufficient balance!" << endl;
        } else if (amount > 0) {
            balance -= amount;
            cout << "Withdrawn: " << amount << endl;
        }
    }
 
    void display() const {
        cout << "Account: " << accountNo
             << " | Owner: " << owner
             << " | Balance: " << balance << endl;
    }
};
 
int main() {
    BankAccount acc1("Alice", 1001, 5000.0);
    acc1.deposit(2000);
    acc1.withdraw(1500);
    acc1.display();
 
    BankAccount acc2("Bob", 1002);   // balance defaults to 0
    acc2.deposit(3000);
    acc2.display();
 
    return 0;
}

Access Specifiers

class Demo {
private:
    int secret;        // accessible only within class
 
protected:
    int shared;        // accessible within class + derived classes
 
public:
    int open;          // accessible from anywhere
 
    void setSecret(int s) { secret = s; }  // only way to set private
    int getSecret() { return secret; }     // only way to read private
};

10. Built-in Operations on Classes

C++ provides default operations for classes automatically.

Default Operations

#include <iostream>
#include <string>
using namespace std;
 
class Rectangle {
public:
    double length;
    double width;
 
    Rectangle(double l = 0, double w = 0) : length(l), width(w) {}
 
    double area() const { return length * width; }
};
 
int main() {
    Rectangle r1(5.0, 3.0);
 
    // 1. Copy construction (built-in)
    Rectangle r2 = r1;
    cout << r2.area() << endl;   // 15 (same as r1)
 
    // 2. Assignment (built-in)
    Rectangle r3;
    r3 = r1;
    cout << r3.area() << endl;   // 15
 
    // 3. Member access with . operator
    cout << r1.length << endl;   // 5
 
    // 4. Address-of operator
    Rectangle* ptr = &r1;
    cout << ptr->area() << endl; // 15 (arrow operator for pointer)
 
    // 5. sizeof
    cout << sizeof(r1) << endl;  // size in bytes
 
    return 0;
}

11. Assignment Operator and Classes

The default assignment operator does a shallow copy (copies member values). For classes with dynamic memory, you need a custom one.

Default Assignment (Shallow Copy)

#include <iostream>
using namespace std;
 
class Point {
public:
    int x, y;
    Point(int x = 0, int y = 0) : x(x), y(y) {}
};
 
int main() {
    Point p1(3, 4);
    Point p2(0, 0);
 
    p2 = p1;    // default assignment: copies x and y
    cout << p2.x << ", " << p2.y << endl;  // 3, 4
 
    p2.x = 100;    // modifying p2 does NOT affect p1
    cout << p1.x << endl;   // still 3
 
    return 0;
}

Custom Assignment Operator (Deep Copy)

#include <iostream>
#include <cstring>
using namespace std;
 
class MyString {
private:
    char* data;
    int length;
 
public:
    MyString(const char* str = "") {
        length = strlen(str);
        data = new char[length + 1];
        strcpy(data, str);
    }
 
    // Copy constructor
    MyString(const MyString& other) {
        length = other.length;
        data = new char[length + 1];
        strcpy(data, other.data);
    }
 
    // Custom assignment operator
    MyString& operator=(const MyString& other) {
        if (this == &other) return *this;  // self-assignment check
 
        delete[] data;    // free old memory
        length = other.length;
        data = new char[length + 1];
        strcpy(data, other.data);
        return *this;
    }
 
    void display() const { cout << data << endl; }
 
    ~MyString() { delete[] data; }
};
 
int main() {
    MyString s1("Hello");
    MyString s2("World");
 
    s2 = s1;      // custom deep copy
    s1.display();  // Hello
    s2.display();  // Hello (independent copy)
    return 0;
}

12. Class Scope

Class scope rules determine where names (members) can be accessed.

#include <iostream>
using namespace std;
 
int x = 100;    // global x
 
class Demo {
    int x;      // class member x (different from global)
 
public:
    Demo(int val) : x(val) {}
 
    void show() {
        cout << "Member x: " << x << endl;       // class member
        cout << "Global x:  " << ::x << endl;    // :: = global scope
    }
};
 
class Counter {
    static int count;    // class-level (shared by all objects)
    int id;
 
public:
    Counter() {
        count++;
        id = count;
    }
    static int getCount() { return count; }  // static member function
    int getId() const { return id; }
};
 
int Counter::count = 0;    // define static member outside class
 
int main() {
    Demo d(42);
    d.show();
 
    Counter c1, c2, c3;
    cout << "Total objects: " << Counter::getCount() << endl;  // 3
    cout << "c2 id: " << c2.getId() << endl;                   // 2
    return 0;
}

13. Reference Parameters and Class Objects

Using references to pass and return class objects efficiently (avoids copying).

#include <iostream>
#include <string>
using namespace std;
 
class Student {
public:
    string name;
    double gpa;
 
    Student(string n, double g) : name(n), gpa(g) {}
 
    void display() const {
        cout << name << " (GPA: " << gpa << ")" << endl;
    }
};
 
// Pass by const reference (read-only, no copy)
void printStudent(const Student& s) {
    s.display();
}
 
// Pass by reference (can modify)
void updateGPA(Student& s, double newGPA) {
    s.gpa = newGPA;
}
 
// Return by reference (careful: don't return local variable!)
Student& getBetter(Student& a, Student& b) {
    return (a.gpa >= b.gpa) ? a : b;
}
 
int main() {
    Student s1("Alice", 3.8);
    Student s2("Bob", 3.5);
 
    printStudent(s1);         // no copy made
    updateGPA(s2, 3.9);
    getBetter(s1, s2).display();  // Bob (GPA: 3.9)
    return 0;
}

14. Member Functions

Functions defined inside (or associated with) a class.

Inside vs Outside Class Definition

#include <iostream>
using namespace std;
 
class Circle {
private:
    double radius;
 
public:
    // Defined INSIDE class (implicitly inline)
    Circle(double r) : radius(r) {}
 
    // Declared inside, defined OUTSIDE using ::
    double area() const;
    double circumference() const;
    void scale(double factor);
};
 
// Definitions outside class using scope resolution operator ::
double Circle::area() const {
    return 3.14159 * radius * radius;
}
 
double Circle::circumference() const {
    return 2 * 3.14159 * radius;
}
 
void Circle::scale(double factor) {
    radius *= factor;    // can access private member
}
 
int main() {
    Circle c(5.0);
    cout << "Area: " << c.area() << endl;
    cout << "Circumference: " << c.circumference() << endl;
    c.scale(2.0);
    cout << "Scaled area: " << c.area() << endl;
    return 0;
}

const Member Functions

class Temperature {
    double celsius;
public:
    Temperature(double c) : celsius(c) {}
 
    double toCelsius() const { return celsius; }       // const: won't modify
    double toFahrenheit() const { return celsius * 9.0/5.0 + 32; }
    void setCelsius(double c) { celsius = c; }         // non-const: modifies
};

Static Member Functions

#include <iostream>
using namespace std;
 
class MathUtils {
public:
    static double square(double x) { return x * x; }
    static double cube(double x)   { return x * x * x; }
    static bool isEven(int n)      { return n % 2 == 0; }
};
 
int main() {
    // Call without creating an object
    cout << MathUtils::square(4) << endl;   // 16
    cout << MathUtils::cube(3) << endl;     // 27
    cout << MathUtils::isEven(7) << endl;   // 0 (false)
    return 0;
}

15. Accessor and Mutator Functions

Accessor (getter): reads private data Mutator (setter): modifies private data with optional validation

#include <iostream>
#include <string>
using namespace std;
 
class Person {
private:
    string name;
    int age;
    string email;
 
public:
    Person() : name(""), age(0), email("") {}
    Person(string name, int age, string email) {
        setName(name);
        setAge(age);
        setEmail(email);
    }
 
    // --- Accessors (getters) ---
    string getName()  const { return name; }
    int    getAge()   const { return age; }
    string getEmail() const { return email; }
 
    // --- Mutators (setters) with validation ---
    void setName(const string& n) {
        if (!n.empty())
            name = n;
        else
            cout << "Name cannot be empty!" << endl;
    }
 
    void setAge(int a) {
        if (a >= 0 && a <= 150)
            age = a;
        else
            cout << "Invalid age: " << a << endl;
    }
 
    void setEmail(const string& e) {
        if (e.find('@') != string::npos)
            email = e;
        else
            cout << "Invalid email!" << endl;
    }
 
    void display() const {
        cout << "Name: " << name
             << " | Age: " << age
             << " | Email: " << email << endl;
    }
};
 
int main() {
    Person p("Alice", 25, "alice@example.com");
    p.display();
 
    p.setAge(200);           // Invalid age: 200
    p.setEmail("notvalid");  // Invalid email!
    p.setAge(30);
    cout << "Age: " << p.getAge() << endl;   // 30
    return 0;
}

16. Constructors

A constructor is a special member function that is automatically called when an object is created. Same name as the class, no return type.

Types of Constructors

#include <iostream>
#include <string>
using namespace std;
 
class Box {
private:
    double length, width, height;
 
public:
    // 1. Default Constructor (no parameters)
    Box() : length(1.0), width(1.0), height(1.0) {
        cout << "Default constructor called" << endl;
    }
 
    // 2. Parameterized Constructor
    Box(double l, double w, double h) : length(l), width(w), height(h) {
        cout << "Parameterized constructor called" << endl;
    }
 
    // 3. Copy Constructor
    Box(const Box& other) : length(other.length),
                             width(other.width),
                             height(other.height) {
        cout << "Copy constructor called" << endl;
    }
 
    double volume() const { return length * width * height; }
 
    void display() const {
        cout << "Box(" << length << " x " << width << " x " << height << ")" << endl;
    }
};
 
int main() {
    Box b1;                   // Default
    Box b2(2.0, 3.0, 4.0);   // Parameterized
    Box b3 = b2;              // Copy
    Box b4(b2);               // Also copy
 
    b1.display();  // Box(1 x 1 x 1)
    b2.display();  // Box(2 x 3 x 4)
    b3.display();  // Box(2 x 3 x 4)
    return 0;
}

Constructor Initializer List (Preferred)

class Employee {
    string name;
    int id;
    const string department;    // const member MUST use initializer list
    int& refId;                 // reference MUST use initializer list
 
public:
    // Initializer list: colon after ), member(value) pairs
    Employee(string n, int i, string dept, int& ref)
        : name(n), id(i), department(dept), refId(ref) {
        // body (optional additional logic)
    }
};

17. Default Constructor

A constructor that can be called with no arguments (either has no parameters, or all parameters have defaults).

#include <iostream>
#include <string>
using namespace std;
 
class Matrix {
private:
    int rows, cols;
    int data[10][10];
 
public:
    // Default constructor: initializes to zero matrix
    Matrix() : rows(3), cols(3) {
        for (int i = 0; i < rows; i++)
            for (int j = 0; j < cols; j++)
                data[i][j] = 0;
        cout << "Zero matrix created" << endl;
    }
 
    // Overloaded: custom size
    Matrix(int r, int c) : rows(r), cols(c) {
        for (int i = 0; i < rows; i++)
            for (int j = 0; j < cols; j++)
                data[i][j] = 0;
    }
 
    void setVal(int i, int j, int val) { data[i][j] = val; }
 
    void display() const {
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++)
                cout << data[i][j] << "\t";
            cout << endl;
        }
    }
};
 
int main() {
    Matrix m1;          // default constructor
    Matrix m2(2, 2);    // parameterized
 
    m2.setVal(0, 0, 1);
    m2.setVal(1, 1, 5);
    m2.display();
 
    Matrix arr[3];      // array: default constructor called 3 times
    return 0;
}

Key Rule: If you define ANY constructor, the compiler will NOT generate a default constructor. Use ClassName() = default; to explicitly request one.

class Widget {
    int value;
public:
    Widget(int v) : value(v) {}
    Widget() = default;   // explicitly request default constructor
};

18. Destructors

A destructor is automatically called when an object goes out of scope or is deleted. Used to release resources (memory, files, connections).

#include <iostream>
#include <string>
using namespace std;
 
class FileHandler {
    string filename;
    bool isOpen;
 
public:
    FileHandler(const string& name) : filename(name), isOpen(true) {
        cout << "Opened file: " << filename << endl;
    }
 
    void write(const string& data) {
        if (isOpen)
            cout << "Writing to " << filename << ": " << data << endl;
    }
 
    // Destructor: same name as class, prefixed with ~, no return type, no params
    ~FileHandler() {
        if (isOpen) {
            isOpen = false;
            cout << "Closed file: " << filename << endl;
        }
    }
};
 
class DynamicArray {
    int* data;
    int size;
 
public:
    DynamicArray(int n) : size(n) {
        data = new int[n];    // allocate
        cout << "Array of " << n << " allocated" << endl;
    }
 
    void set(int i, int val) { data[i] = val; }
    int get(int i) const { return data[i]; }
 
    ~DynamicArray() {
        delete[] data;        // MUST free to avoid memory leak
        cout << "Array deallocated" << endl;
    }
};
 
void demo() {
    FileHandler f("report.txt");  // constructor called
    f.write("Hello World");
}   // ← destructor called here automatically when f goes out of scope
 
int main() {
    demo();
 
    {
        DynamicArray arr(5);
        for (int i = 0; i < 5; i++) arr.set(i, i * 10);
        cout << arr.get(2) << endl;   // 20
    }   // ← destructor called here
 
    cout << "main continues..." << endl;
    return 0;
}

Constructor/Destructor Order

#include <iostream>
using namespace std;
 
class Test {
    int id;
public:
    Test(int i) : id(i) { cout << "Constructor " << id << endl; }
    ~Test()              { cout << "Destructor  " << id << endl; }
};
 
int main() {
    Test a(1);
    Test b(2);
    Test c(3);
    return 0;
}
/*
Constructor 1
Constructor 2
Constructor 3
Destructor  3   ← Last in, first out (stack order)
Destructor  2
Destructor  1
*/

Quick Reference Summary

Class Template

class ClassName {
private:
    // data members (attributes)
 
protected:
    // accessible in derived classes
 
public:
    ClassName();                        // default constructor
    ClassName(params);                  // parameterized constructor
    ClassName(const ClassName& other);  // copy constructor
    ~ClassName();                       // destructor
 
    ClassName& operator=(const ClassName& other);  // assignment operator
 
    // Accessors (getters)
    type getMember() const;
 
    // Mutators (setters)
    void setMember(type value);
 
    // Other member functions
    void doSomething();
    static void staticFunction();
};

Key Rules Cheat Sheet

RuleDescription
Constructor nameSame as class name
Destructor name~ClassName()
Constructor return typeNone (not even void)
const member functionCannot modify member variables
static memberShared across all objects
virtual functionEnables runtime polymorphism
Pure virtual = 0Makes class abstract
Accessorget functions, marked const
Mutatorset functions with validation