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
| Part | Description | Example |
|---|---|---|
| Return type | Type of value function returns | int, double, void |
| Function name | Identifier for the function | add, display |
| Parameter list | Input values (can be empty) | (int a, int b) |
| Function body | Statements inside {} | { return a+b; } |
| Return statement | Sends value back to caller | return 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
| Rule | Description |
|---|---|
| Constructor name | Same as class name |
| Destructor name | ~ClassName() |
| Constructor return type | None (not even void) |
const member function | Cannot modify member variables |
static member | Shared across all objects |
virtual function | Enables runtime polymorphism |
Pure virtual = 0 | Makes class abstract |
| Accessor | get functions, marked const |
| Mutator | set functions with validation |