Smart Pointers: unique, shared, weak
Smart pointer-এ গভীর জ্ঞান
1. The Three Smart Pointers
| Type | Ownership | Cost |
|---|---|---|
std::unique_ptr<T> | Single owner | ~Zero (same as raw pointer) |
std::shared_ptr<T> | Multiple owners (reference counted) | Atomic increment + control block |
std::weak_ptr<T> | Non-owning observer (breaks cycles) | Like shared, but no ref count |
2. unique_ptr — Default Choice
#include <iostream>
#include <memory>
struct Widget {
int id;
Widget(int i) : id(i) { std::cout << "Widget " << id << " born\n"; }
~Widget() { std::cout << "Widget " << id << " dies\n"; }
};
void use(const Widget& w) { std::cout << "using " << w.id << "\n"; }
int main() {
auto p = std::make_unique<Widget>(42);
use(*p);
auto p2 = std::move(p); // transfer ownership
// p is now nullptr
}
3. shared_ptr — When You Need Sharing
#include <iostream>
#include <memory>
int main() {
auto p1 = std::make_shared<int>(42);
{
auto p2 = p1;
std::cout << "count = " << p1.use_count() << "\n"; // 2
}
std::cout << "count = " << p1.use_count() << "\n"; // 1
}
unique_ptr + raw pointer (non-owning) is the right design.
4. weak_ptr — Breaking Cycles
If two shared_ptrs reference each other (a cycle), the count never reaches zero → leak. weak_ptr doesn't increment the count.
struct Node {
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev; // don't own backwards
};
// Use:
if (auto sp = node->prev.lock()) {
// sp is shared_ptr while alive
}
5. Custom Deleters
For non-memory resources (files, sockets):
#include <cstdio>
#include <memory>
std::unique_ptr<FILE, decltype(&std::fclose)>
f(std::fopen("data.txt", "r"), &std::fclose);
// auto-closes on scope exit
6. Decision Guide
- Sole owner →
unique_ptr(default) - Truly shared →
shared_ptr(audit your design first!) - Observer (cache, parent ref) → raw pointer or
weak_ptr - Owns a resource that needs cleanup → smart ptr with custom deleter
7. Practice Problems
- Create a unique_ptr<int> with value 7.
✨ Show Answer
auto p = std::make_unique<int>(7); - Why prefer
make_unique?✨ Show Answer
Exception safety + concise.
std::unique_ptr<T>(new T)can leak inf(unique_ptr(new T), g())if g throws. - Transfer unique_ptr to a function.
✨ Show Answer
void take(std::unique_ptr<T> p); take(std::move(my_unique)); - Pass a unique_ptr to a function that doesn't own it.
✨ Show Answer
void use(const T& obj); // pass *pOr pass a raw pointer if it can be null. Don't take a unique_ptr by reference.
- Why is shared_ptr more expensive?
✨ Show Answer
Atomic ref count increment/decrement on copy/destroy. Plus an extra control block allocation (mostly avoided by make_shared).
- When does shared_ptr leak?
✨ Show Answer
Reference cycles. Two shared_ptrs holding each other never reach 0. Break with
weak_ptr. - Get a shared_ptr's count.
✨ Show Answer
p.use_count(); - Lock a weak_ptr to use the object.
✨ Show Answer
if (auto sp = wp.lock()) { use(*sp); } - Create a shared_ptr with a custom deleter.
✨ Show Answer
std::shared_ptr<FILE> f(std::fopen("x", "r"), [](FILE* p){ std::fclose(p); }); - When is unique_ptr essentially free?
✨ Show Answer
Always. It's a wrapper around a single pointer with a destructor. No runtime overhead vs raw pointer.
- Convert unique_ptr to shared_ptr.
✨ Show Answer
std::shared_ptr<T> sp = std::move(up); - Reset a unique_ptr to nullptr.
✨ Show Answer
p.reset(); // or p = nullptr; - Get raw pointer (non-owning) from a unique_ptr.
✨ Show Answer
T* raw = p.get(); - Why might shared_ptr<T> have a lower performance ceiling than unique_ptr?
✨ Show Answer
Atomic operations on the count are expensive in multi-threaded code. They limit scaling.
- Use
std::enable_shared_from_this— why?✨ Show Answer
Lets a class create a shared_ptr to itself from a member function (
shared_from_this()) without creating a separate, unrelated control block. - vector<unique_ptr<T>> — what does it model?
✨ Show Answer
A collection that owns polymorphic objects. Common pattern for storing different derived types.
Summary
Default to unique_ptr; it's free. Use shared_ptr only when ownership is
genuinely shared. weak_ptr breaks cycles. Use make_unique /
make_shared rather than raw new. Custom deleters extend smart pointers to any resource.