C++ - Smart Pointers & RAII
Overview
Estimated time: 60–80 minutes
Manage resources safely with RAII. Use smart pointers to express ownership. Prefer composition and automatic cleanup over manual new/delete.
Learning Objectives
- Choose unique_ptr for sole ownership; shared_ptr/weak_ptr for shared lifetimes.
- Use make_unique/make_shared and custom deleters.
- Design RAII types to manage resources and invariants automatically.
Prerequisites
unique_ptr basics
#include <memory>
#include <iostream>
int main(){
auto p = std::make_unique<int>(42);
std::cout << *p << "\n";
auto q = std::move(p); // transfer ownership
std::cout << (p ? "p set" : "p null") << "\n";
}
Expected Output:
42
p null
shared_ptr and weak_ptr
#include <memory>
#include <iostream>
int main(){
std::shared_ptr<int> a = std::make_shared<int>(7);
std::weak_ptr<int> w = a;
std::cout << a.use_count() << "\n"; // 1
if (auto s = w.lock()) std::cout << *s << "\n"; // 7
}
Custom deleter
#include <cstdio>
#include <memory>
struct FileCloser { void operator()(std::FILE* f) const { if (f) std::fclose(f); } };
int main(){
std::unique_ptr<std::FILE, FileCloser> f(std::fopen("data.txt", "w"));
if (f) std::fputs("hello", f.get());
} // RAII closes file
Design guidelines
- Prefer unique_ptr for ownership; use shared_ptr only when sharing lifetime is required.
- Avoid cycles with shared_ptr; break with weak_ptr.
- Favor make_unique/make_shared to reduce allocation overhead and exceptions hazards.
Beginner Boosters
#include
#include
std::unique_ptr make_counter(){ return std::make_unique(0); }
void bump(std::unique_ptr c){ (*c)++; std::cout << *c << "\n"; }
int main(){
auto c = make_counter();
bump(std::move(c)); // prints 1
std::cout << (c?"alive":"moved") << "\n"; // moved
}
Expected Output:
1
moved
#include
#include
int main(){
std::weak_ptr w;
{
auto s = std::make_shared(5);
w = s; // w observes s
} // s destroyed
std::cout << (w.lock() ? "alive" : "expired") << "\n";
}
Expected Output: expired
Common Pitfalls
- Manually calling delete on raw pointers managed by smart pointers—never do this.
- Creating shared_ptr cycles (A holds shared_ptr to B and B to A); memory leak until broken by weak_ptr.
Checks for Understanding
- When to prefer unique_ptr vs shared_ptr?
- How do you prevent shared_ptr cycles?
Show answers
- unique_ptr for single ownership; shared_ptr when multiple entities must share lifetime.
- Use weak_ptr for back-references or to break cycles.
Exercises
- Wrap a POSIX handle (FILE*/fd) into an RAII type using unique_ptr with a custom deleter.
- Build a simple graph node with shared_ptr neighbors and weak_ptr back-links to avoid cycles.