Smart Pointers & RAII - Key Concepts and Patterns
Smart Pointers & RAII - Key Concepts and Patterns
1
void foo() {
auto buf = std::make_unique<char[]>(1024); // buffer deleted when foo()
exits
// ... use buf.get() ...
}
• Caution: Avoid unnecessary use. If the object does not need to live beyond a single owner,
unique_ptr is preferable. Overusing shared_ptr for everything can lead to hidden
performance costs and maintenance issues 9 .
2
• Example:
• Benefit: weak_ptr prevents memory leaks in graphs or observer patterns by ensuring that objects
can be collected when no strong ( shared_ptr ) references remain 12 .
Best Practices
• Prefer unique_ptr by Default: As Herb Sutter summarizes, “prefer unique_ptr by default” 9 .
Only switch to shared_ptr when you truly need shared ownership. A unique_ptr can later be
moved into a shared_ptr if sharing becomes necessary, so start simple. 9 6
3
• Employ Rule of Zero: In your own classes, prefer to hold resources in smart-pointer members rather
than writing manual copy/move/destructors. Following Rule of Zero 4 means you often need no
special member functions at all.
• RAII for All Resources: Extend the RAII pattern beyond memory. Wrap file descriptors, sockets,
mutex locks, GPU buffers, etc., in objects whose destructors release the resource. (Examples:
std::lock_guard<std::mutex> , or a custom class that closes a file in its destructor.) This
practice prevents resource leaks in all realms of system or high-performance programming 1 .
Anti-Patterns to Avoid
• Overusing shared_ptr : Treat shared_ptr as a tool only for when multiple ownership is
genuinely required. Using it everywhere leads to hidden costs. (For example, throwing
shared_ptr<T> into STL containers or globals when a unique_ptr or raw pointer would suffice
is wasteful.) 9
• Manual new without Smart Pointers: Avoid owning raw pointers. If you allocate with new ,
immediately wrap it in a smart pointer. Otherwise an exception or return path could leak memory.
(Factory functions like make_unique prevent this common pitfall.) 8
• Circular shared_ptr : Never allow two objects to hold shared_ptr s to each other without a
weak_ptr . This cycle will keep them alive indefinitely (leak memory) 12 .
• Defining Destructors Unnecessarily: Don’t write a destructor just to debug or log unless needed.
Even a trivial destructor suppresses move operations (Rule of Five) 4 . If a class only holds smart
pointers, let defaults handle destruction (Rule of Zero).
• Ignoring get() / .release() : When interfacing with APIs that expect raw pointers, use get()
to observe the pointer or release() to give up ownership carefully. Misusing these can cause
double-deletes or leaks.
void loadData() {
auto data = std::make_unique<uint8_t[]>(dataSize); // deleted
automatically
// fill data...
}
• Resource Handles: Wrap OS resources in RAII objects. E.g. a FileRAII class that opens a file in its
ctor and closes in its dtor, or use std::unique_ptr<FILE, decltype(&fclose)> as a quick
wrapper. This prevents forgetting to close files even on early returns.
• Shared Ownership Example: A subscriber list or cache might use std::shared_ptr . For
instance, a callback registry can hold std::shared_ptr<Listener> so listeners are kept alive as
long as registered.
• Breaking Cycles: In a tree or graph structure, child nodes might hold a std::weak_ptr to their
parent to avoid strong cycles. For example:
4
struct Node {
std::weak_ptr<Node> parent; // does not keep parent alive
std::shared_ptr<Node> child; // keeps child alive
};
This way the parent can be destroyed when appropriate even though the child points back to it.
• Locking: std::lock_guard<std::mutex> is a canonical RAII example in multithreading: it locks
the mutex on construction and automatically unlocks it on scope exit. This prevents deadlocks from
forgotten unlocks.
By adhering to RAII and using smart pointers judiciously (following the Rule of Zero and best practices), C++
code can be both safe from leaks and efficient. Smart pointers let you avoid manual delete entirely in
most code, shifting error-prone cleanup into well-tested library code (destructors and control blocks) 2
9 .
1 2 3 RAII - cppreference.com
https://en.cppreference.com/w/cpp/language/raii
5 7 std::unique_ptr - cppreference.com
https://en.cppreference.com/w/cpp/memory/unique_ptr
12 13 22.7 — Circular dependency issues with std::shared_ptr, and std::weak_ptr – Learn C++
https://www.learncpp.com/cpp-tutorial/circular-dependency-issues-with-stdshared_ptr-and-stdweak_ptr/