Dynamic memory, RAII, and smart pointers
Understand dynamic object lifetime, ownership, RAII, and modern C++ smart pointers.
Learning objectives
- Distinguish automatic and dynamic object lifetime.
- Explain why manual new/delete is error-prone.
- Describe RAII and ownership.
- Choose between unique_ptr, shared_ptr, and weak_ptr at a basic level.
Prerequisites: understand pointers, classes, constructors and destructors, and object lifetime. Review Pointers, Classes and objects, Constructors and destructors, and Scope and lifetime first. Examples use C++17.
Why dynamic lifetime?
Some objects need to live beyond the block in which they were created, or their number is only known while the program runs. C++ supports dynamic storage, but manually managing it creates responsibility: every successful allocation must be released exactly once, and no pointer may be used after the object is destroyed.
Manual allocation: understand, but do not prefer
#include <iostream>\nint* value = new int{42};
std::cout << *value << '\n';
delete value;
value = nullptr;new creates an object with dynamic storage duration and returns a pointer. delete ends that object's lifetime and releases its storage. Forgetting delete can leak memory; deleting twice or using a pointer afterward is also wrong. This syntax is important to understand, but modern C++ normally uses resource-managing types instead.
RAII: let object lifetime manage resources
RAII means Resource Acquisition Is Initialization. A resource is acquired by an object's construction and released by its destructor. The same idea manages memory, files, locks, and other resources. Standard containers such as std::vector already use RAII internally.
Unique ownership with unique_ptr
#include <iostream>
#include <memory>
int main() {
auto value = std::make_unique<int>(42);
std::cout << *value << '\n';
}std::unique_ptr represents exclusive ownership: one unique_ptr owns the object. When the smart pointer is destroyed, it automatically deletes the object. It cannot be copied, but ownership can be moved.
Shared ownership with shared_ptr
#include <memory>\nauto first = std::make_shared<int>(42); auto second = first; // shared ownership // object is destroyed when the last shared owner is gone
std::shared_ptr uses shared ownership. A control block tracks how many shared owners remain. Use it only when the design genuinely requires shared lifetime; reference counting adds cost and can create cycles.
weak_ptr breaks ownership cycles
std::weak_ptr observes an object managed by shared_ptr without increasing the shared ownership count. It is useful for non-owning links that must not keep an object alive, such as parent/child or cache relationships. Call lock() to obtain a temporary shared_ptr if the object still exists.
Ownership rules of thumb
- Prefer automatic objects and standard containers such as vector and string.
- Use unique_ptr for one clear owner of a dynamically allocated object.
- Use shared_ptr only when multiple owners truly share lifetime.
- Use weak_ptr for a non-owning observation of a shared object.
- Do not use raw pointers to express ownership unless you are implementing a low-level resource abstraction with a clear contract.
Common mistakes
- Forgetting to delete manually allocated memory.
- Deleting an object twice or using it after delete.
- Creating shared_ptr ownership cycles that prevent destruction.
- Assuming a raw pointer automatically owns the object it points to.
- Using shared_ptr everywhere instead of designing ownership.
Practice
- Replace a simple new/delete example with make_unique.
- Explain why unique_ptr cannot be copied but can be moved.
- Draw a shared ownership count for two shared_ptr variables.
- Explain how weak_ptr can break a parent-child ownership cycle.
Practice solutions
Show worked solutions
#include <memory> int main() { auto value = std::make_unique<int>(42); }make_uniquerequires C++14 or later and the smart pointer releases the object automatically.- Copying a unique_ptr would create two exclusive owners. Moving transfers ownership to the destination and leaves the source empty.
- After
auto first = std::make_shared<int>(7);, the strong owner count is one. Afterauto second = first;, it is two. When one owner is destroyed, it decreases by one. - Store the back-reference as
std::weak_ptr<Parent>instead ofstd::shared_ptr<Parent>. A weak_ptr does not increase the strong ownership count; calllock()to obtain a temporary shared_ptr if the object still exists.
Check your understanding
Which smart pointer should you choose when there is one clear owner?
Answer: Usually std::unique_ptr, because it expresses exclusive ownership and cleans up automatically.
Summary
Prefer RAII and standard-library resource managers. Use smart pointers to express ownership clearly: unique for one owner, shared for multiple owners, and weak for non-owning observation of shared objects.