Object Oriented ProgrammingUnit 99 min read
Dynamic Memory Allocation & Smart Pointers in C++: new/delete, RAII, and Memory Leaks
Unit 9 of Object Oriented Programming explores how C++ manages memory at runtime using new/delete, smart pointers (uniqueptr, sharedptr), and RAII principles—critical for building scalable apps like eSewa’s transaction logs or Pathao’s ride-matching queues.
TAKEAWAYS:
- Dynamic allocation (
new/delete) creates objects at runtime but risks memory leaks if mismanaged. - Smart pointers (
unique_ptr,shared_ptr) automate cleanup via RAII, preventing leaks in modern C++. - RAII (Resource Acquisition Is Initialization) ties resource lifetime to object scope, used in every C++ library.
new[]/delete[]handle arrays dynamically, but mismatched calls cause undefined behavior.- Custom allocators (e.g.,
std::pmr) optimize memory for high-performance systems like game engines. - Valgrind (Linux) or Visual Studio’s Debugger detects leaks in real-world projects.
Core Concepts: How Memory Works in C++
1. Static vs. Dynamic Memory
C++ allocates memory in two ways:
- Static memory: Fixed at compile-time (global variables, local
staticvariables). - Dynamic memory: Allocated at runtime using
new/delete(ormalloc/freein C-style).
classDiagram
class Memory {
+static: Fixed size, compile-time
+dynamic: Variable size, runtime via `new`
}
class Allocation {
+new: Allocates single object
+new[]: Allocates array
+delete: Frees single object
+delete[]: Frees array
}
Memory --> Allocation : "Uses"Why dynamic memory?
- Flexibility: Create objects only when needed (e.g., Pathao’s ride queue grows/shrinks).
- Efficiency: Avoid wasting memory for rarely used objects (e.g., Daraz’s order history).
2. The new and delete Operators
Allocation
int* ptr = new int(42); // Allocates memory for an int initialized to 42
What happens?
flowchart LR
A["new int(42)"] --> B["Heap: [42]"]
B --> C["ptr → Heap[0]"]Deallocation
delete ptr; // Frees memory
State after delete:
flowchart LR
A["ptr → NULL"] --> B["Heap: [FREED]"]Key Rules:
- Always pair
newwithdelete(or use smart pointers). - Never
deletethe same pointer twice (crash!). - Never
deletea pointer returned bynew[]withdelete(memory corruption).
In the Real World
eSewa’s Transaction Logs
- Uses dynamic arrays (
new[]) to store thousands of payment records. - Why? Static arrays can’t grow; dynamic arrays scale with user transactions.
- Risk: Forgetting
delete[]leaks memory, crashing the system during peak hours.
- Uses dynamic arrays (
Pathao’s Ride-Matching Queue
- Dynamically allocates
DriverandPassengerobjects for real-time matching. - Optimization: Uses
std::vector(dynamic array) to avoid manualnew/delete.
- Dynamically allocates
Ncell’s Call Routing
- Smart pointers (
shared_ptr) manage network connection objects. - Why? Multiple threads share call data;
shared_ptrensures cleanup when all threads finish.
- Smart pointers (
3. Memory Leaks: The Silent Killer
A memory leak occurs when allocated memory is no longer accessible (e.g., lost pointer). Example: Lost Pointer
void leak() {
int* ptr = new int(100);
ptr = new int(200); // Original 100 is lost!
}
Visualization:
flowchart TD
A["ptr = new int(100)"] --> B["Heap: [100]"]
B --> C["ptr = new int(200)"]
C --> D["Heap: [100 (LEAKED)], [200]"]How to Detect?
- Valgrind (Linux):
valgrind --leak-check=full ./your_program - Visual Studio Debugger: Enable "Detect Memory Leaks" in project settings.
4. Smart Pointers: Automatic Cleanup
Smart pointers manage memory automatically using RAII (Resource Acquisition Is Initialization).
Types of Smart Pointers
| Pointer Type | Header | Use Case | Example |
|---|---|---|---|
std::unique_ptr |
<memory> |
Exclusive ownership | auto ptr = std::make_unique<int>(42); |
std::shared_ptr |
<memory> |
Shared ownership (reference counting) | auto ptr = std::make_shared<int>(42); |
std::weak_ptr |
<memory> |
Break circular references | std::weak_ptr<int> weak = ptr; |
Example: unique_ptr
#include <memory>
void safeAllocation() {
auto ptr = std::make_unique<int>(100); // Automatically deleted at scope end
// No need for manual delete!
}
State after scope ends:
flowchart LR
A["ptr goes out of scope"] --> B["Heap: [FREED]"]shared_ptr and Reference Counting
auto ptr1 = std::make_shared<int>(50);
auto ptr2 = ptr1; // Reference count = 2
Visualization:
flowchart TD
A["ptr1 → Heap[50]"] --> B["ptr2 → Heap[50]"]
B --> C["Ref Count: 2"]When shared_ptr deletes:
flowchart LR
A["ptr1 and ptr2 go out of scope"] --> B["Ref Count: 0 → Heap[FREED]"]5. Dynamic Arrays: new[] and delete[]
Allocation
int* arr = new int[5]; // Allocates 5 ints
arr[0] = 10; arr[1] = 20;
Heap State:
flowchart TD
A["new int[5]"] --> B["Heap: [10, 20, 0, 0, 0]"]Deallocation
delete[] arr; // Must use delete[], not delete!
Warning:
delete[] arr; // Correct
delete arr; // CRASH: Undefined behavior!
6. RAII: The C++ Way to Manage Resources
RAII Principle:
"Tie a resource’s lifetime to an object’s lifetime."
Example: File Handling with RAII
#include <fstream>
void safeFileWrite() {
std::ofstream file("data.txt"); // Opens file
file << "Hello, RAII!"; // Writes data
} // File automatically closed here!
Why RAII?
- Prevents leaks (e.g., forgotten
file.close()). - Works for all resources: files, sockets, locks.
7. Custom Allocators and std::pmr
For high-performance apps (e.g., game engines), use polymorphic allocators:
#include <memory_resource>
std::pmr::memory_resource* mr = std::pmr::get_default_resource();
auto ptr = std::pmr::make_unique<int>(mr, 42);
Use Case:
- Unreal Engine: Uses custom allocators for memory pools.
- NEPSE’s Trading System: Optimizes memory for high-frequency trades.
Worked Example: Bank Loan Calculator with Dynamic Memory
Problem: A bank allocates loan objects dynamically. Calculate interest for 3 loans. Solution:
#include <iostream>
#include <memory>
class Loan {
public:
Loan(double amount, double rate) : amount(amount), rate(rate) {}
double calculateInterest(int years) {
return amount * rate * years;
}
private:
double amount, rate;
};
int main() {
// Dynamic allocation with unique_ptr
auto loan1 = std::make_unique<Loan>(100000, 0.05);
auto loan2 = std::make_unique<Loan>(500000, 0.07);
auto loan3 = std::make_unique<Loan>(200000, 0.06);
std::cout << "Interest for Loan 1: "
<< loan1->calculateInterest(5) << std::endl;
// Output: 25000
return 0;
}
Heap State After Allocation:
flowchart TD
A["loan1 → Loan(100k, 5%)"] --> B["loan2 → Loan(500k, 7%)"]
B --> C["loan3 → Loan(200k, 6%)"]Why This Matters for Banks:
- Scalability: Loans are created/destroyed as customers join/leave.
- Safety:
unique_ptrensures no memory leaks in high-stakes systems.
Common Pitfalls and How to Avoid Them
| Pitfall | Solution | Example |
|---|---|---|
Forgetting delete |
Use smart pointers (unique_ptr) |
auto ptr = std::make_unique<int>(42); |
Mismatched new[]/delete |
Always use delete[] for arrays |
delete[] arr; |
| Dangling pointers | Reset pointers to nullptr after delete |
delete ptr; ptr = nullptr; |
Circular references in shared_ptr |
Use weak_ptr to break cycles |
std::weak_ptr<int> weak = ptr; |
Exam Tip
- Always explain the difference between
new/deleteand smart pointers in answers.- "
newrequires manual cleanup;unique_ptrautomates it via RAII."
- "
- Draw heap diagrams for dynamic allocation questions (e.g., show state before/after
delete). - Mention RAII whenever resources are involved (files, memory, locks).
- For polymorphism questions, link to dynamic binding (e.g., virtual functions use dynamic memory).
- Practice tracing:
- Start with a raw pointer example, then rewrite it with
shared_ptr. - Show how reference counts change.
- Start with a raw pointer example, then rewrite it with
Quick Revision Table
| Concept | Syntax Example | Key Point |
|---|---|---|
| Dynamic Allocation | int* ptr = new int(10); |
Use delete to free. |
| Dynamic Array | int* arr = new int[5]; |
Use delete[] to free. |
unique_ptr |
auto ptr = std::make_unique<int>(10); |
Exclusive ownership. |
shared_ptr |
auto ptr = std::make_shared<int>(10); |
Reference counting. |
| RAII | std::ofstream file("data.txt"); |
Resource tied to object lifetime. |
| Memory Leak | new int; // No delete |
Use Valgrind to detect. |
Final Challenge: Debug the Leak
void buggyFunction() {
int* arr = new int[10];
arr[0] = 100;
// Missing delete[]!
}
Fix:
void fixedFunction() {
auto arr = std::make_unique<int[]>(10); // Automatically freed
arr[0] = 100;
}
Heap After Fix:
flowchart LR
A["arr goes out of scope"] --> B["Heap: [FREED]"]Based on the TU BIT syllabus for Object Oriented Programming (BIT153), unit 9.
Discussion
Loading…