Module 24: Smart Pointers in C++
Introduction
Memory management is one of the most important topics in C++. Before C++11, programmers had to manually allocate and free memory using new and delete. Forgetting to free memory caused memory leaks, while deleting memory twice caused undefined behaviour.
To solve these problems, C++11 introduced Smart Pointers.
Smart pointers automatically manage dynamically allocated memory using RAII (Resource Acquisition Is Initialization).
Instead of worrying about calling delete, smart pointers release memory automatically when it is no longer needed.
Learning Objectives
After completing this module, you will understand:
- Why smart pointers are needed
- Problems with raw pointers
- What ownership means
unique_ptrshared_ptrweak_ptr- Memory management using RAII
- Best practices
Why Do We Need Smart Pointers?
Consider this example:
1#include <iostream> 2using namespace std; 3 4int main() { 5 int* ptr = new int(100); 6 7 cout << *ptr << endl; 8 9 delete ptr; 10}
Output
100
Looks fine.
But what if we forget delete?
1int* ptr = new int(100); 2 3// forgot delete
Memory is never released.
This is called a Memory Leak.
Example
Suppose this function is called 1 million times.
1void process() 2{ 3 int* ptr = new int(50); 4 5 // forgot delete 6}
Every call allocates memory.
None is released.
Eventually the application consumes huge amounts of RAM.
Problems with Raw Pointers
Raw pointers can cause:
- Memory leaks
- Double delete
- Dangling pointers
- Complex ownership
- Difficult debugging
Example
1int* ptr = new int(10); 2 3delete ptr; 4 5delete ptr;
Deleting twice causes undefined behaviour.
What is Ownership?
Ownership means:
Who is responsible for deleting the object?
Example
1Person 2 | 3 owns 4 | 5Object
Only the owner should destroy the object.
Smart pointers manage ownership automatically.
Types of Smart Pointers
C++ provides three smart pointers.
| Smart Pointer | Ownership | Multiple Owners | Automatically Deletes |
|---|---|---|---|
| unique_ptr | Single | No | Yes |
| shared_ptr | Shared | Yes | Yes |
| weak_ptr | Non-owning | No | No |
RAII (Resource Acquisition Is Initialization)
RAII means:
Acquire resources inside constructors.
Release resources inside destructors.
Smart pointers use RAII internally.
Example
Object Created
↓
Smart Pointer Owns Object
↓
Smart Pointer Destroyed
↓
Object Automatically Deleted
unique_ptr
A unique_ptr has exactly one owner.
Only one pointer can own the object.
Cannot be copied.
Can be moved.
Header
1#include <memory>
Creating unique_ptr
1#include <iostream> 2#include <memory> 3 4using namespace std; 5 6int main() 7{ 8 unique_ptr<int> ptr = make_unique<int>(100); 9 10 cout << *ptr << endl; 11}
Output
100
No need for delete.
Memory is automatically released.
How unique_ptr Works
unique_ptr
|
▼
+------------+
| Object |
+------------+
Program Ends
↓
Object Deleted Automatically
Copying unique_ptr
Not allowed.
1unique_ptr<int> p1 = make_unique<int>(10); 2 3unique_ptr<int> p2 = p1;
Compiler Error
Copy constructor deleted
Because two owners are not allowed.
Moving unique_ptr
Ownership can be transferred.
1unique_ptr<int> p1 = make_unique<int>(50); 2 3unique_ptr<int> p2 = move(p1); 4 5cout << *p2;
After moving:
p1 → nullptr
p2 → owns object
Example
1#include <iostream> 2#include <memory> 3 4using namespace std; 5 6int main() 7{ 8 unique_ptr<string> name = make_unique<string>("Tech3Space"); 9 10 cout << *name << endl; 11}
Output
Tech3Space
unique_ptr with Class
1#include <iostream> 2#include <memory> 3 4using namespace std; 5 6class Student 7{ 8public: 9 10 Student() 11 { 12 cout << "Constructor\n"; 13 } 14 15 ~Student() 16 { 17 cout << "Destructor\n"; 18 } 19}; 20 21int main() 22{ 23 unique_ptr<Student> s = make_unique<Student>(); 24 25}
Output
Constructor
Destructor
Destructor is automatically called.
shared_ptr
Sometimes multiple objects need to share ownership.
Example
Window
↓
Image
↓
Cache
↓
Renderer
All need access.
Use shared_ptr.
Creating shared_ptr
1#include <iostream> 2#include <memory> 3 4using namespace std; 5 6int main() 7{ 8 shared_ptr<int> ptr = make_shared<int>(100); 9 10 cout << *ptr << endl; 11}
Output
100
Reference Count
shared_ptr keeps a reference count.
Example
1shared_ptr<int> p1 = make_shared<int>(10); 2 3shared_ptr<int> p2 = p1; 4 5shared_ptr<int> p3 = p2;
Reference count
3
When one pointer is destroyed
2
When count becomes
0
Object is automatically deleted.
use_count()
1#include <iostream> 2#include <memory> 3 4using namespace std; 5 6int main() 7{ 8 shared_ptr<int> p1 = make_shared<int>(10); 9 10 cout << p1.use_count() << endl; 11 12 shared_ptr<int> p2 = p1; 13 14 cout << p1.use_count() << endl; 15}
Output
1
2
shared_ptr Diagram
p1 ───┐
p2 ───┼────► Object
p3 ───┘
Reference Count = 3
Problem with shared_ptr
Circular references.
Example
A owns B
B owns A
Reference count never becomes zero.
Memory leak occurs.
weak_ptr
A weak_ptr observes an object managed by shared_ptr.
It does not own the object.
It does not increase the reference count.
Creating weak_ptr
1shared_ptr<int> sp = make_shared<int>(100); 2 3weak_ptr<int> wp = sp;
Reference count remains
1
Accessing weak_ptr
Use lock().
1#include <iostream> 2#include <memory> 3 4using namespace std; 5 6int main() 7{ 8 shared_ptr<int> sp = make_shared<int>(100); 9 10 weak_ptr<int> wp = sp; 11 12 if(auto temp = wp.lock()) 13 { 14 cout << *temp << endl; 15 } 16}
Output
100
Expired Object
1weak_ptr<int> wp; 2 3{ 4 shared_ptr<int> sp = make_shared<int>(50); 5 6 wp = sp; 7} 8 9if(wp.expired()) 10{ 11 cout << "Object deleted"; 12}
Output
Object deleted
Smart Pointer Comparison
| Feature | unique_ptr | shared_ptr | weak_ptr |
|---|---|---|---|
| Ownership | Single | Shared | None |
| Copy Allowed | No | Yes | Yes |
| Move Allowed | Yes | Yes | Yes |
| Reference Count | No | Yes | No |
| Automatic Delete | Yes | Yes | No |
| Fastest | ✅ | ❌ | ✅ |
Memory Management Flow
Allocate Memory
↓
Smart Pointer Owns Object
↓
Use Object
↓
Pointer Goes Out of Scope
↓
Destructor Executes
↓
Memory Released
Real-World Example
Imagine a library.
unique_ptr
One person borrows the only copy of a rare book.
Only one owner.
shared_ptr
Many students read the same online PDF.
Everyone shares access.
weak_ptr
A visitor checks whether the PDF is still available.
The visitor does not own it.
Best Practices
✅ Prefer make_unique() over new
1auto ptr = make_unique<int>(10);
✅ Prefer make_shared()
1auto ptr = make_shared<int>(50);
✅ Avoid raw new and delete
✅ Use weak_ptr to break circular references
✅ Keep ownership simple
Common Mistakes
Forgetting std::move
1unique_ptr<int> p1 = make_unique<int>(10); 2 3unique_ptr<int> p2 = p1;
Wrong.
Use
1unique_ptr<int> p2 = move(p1);
Creating shared_ptr from Same Raw Pointer
Wrong
1int* p = new int(10); 2 3shared_ptr<int> a(p); 4 5shared_ptr<int> b(p);
This creates two separate owners for the same raw pointer and can lead to double deletion.
Correct
1auto a = make_shared<int>(10); 2 3shared_ptr<int> b = a;
Interview Questions
1. Why are smart pointers used?
To automatically manage dynamically allocated memory and prevent memory leaks.
2. What is RAII?
A C++ technique where resources are acquired during object construction and released automatically during destruction.
3. Can unique_ptr be copied?
No. It supports only move semantics.
4. When should shared_ptr be used?
When multiple parts of a program need shared ownership of the same object.
5. Why use weak_ptr?
To observe an object managed by shared_ptr without increasing its reference count, helping avoid circular references.
6. What does use_count() return?
The number of active shared_ptr instances that own the object.
7. What happens when the last shared_ptr is destroyed?
The managed object is automatically deleted.
Module Summary
In this module, you learned:
- Why smart pointers are safer than raw pointers
- The concept of ownership in C++
- How
unique_ptrprovides exclusive ownership - How
shared_ptrenables shared ownership using reference counting - How
weak_ptrprevents circular references - How RAII enables automatic resource management
- Best practices for modern C++ memory management
You are now ready to write safer, cleaner, and more maintainable C++ code using smart pointers.