RAII: Resource Acquisition Is Initialization
Why tying resource cleanup to a destructor makes cleanup automatic and impossible to forget.
What you'll learn
- Explain the RAII idiom: acquiring a resource in a constructor, releasing it in a destructor
- Predict when cleanup happens for an RAII object as control leaves its scope
- Explain why RAII makes cleanup automatic even when a function has multiple exit points
Explanation
RAII (Resource Acquisition Is Initialization) is C++'s central idiom for managing any resource that needs cleanup -- memory, file handles, locks, network connections. The idea: acquire the resource in a constructor, and release it in the matching destructor. Since you already know a local object's destructor runs automatically and deterministically when it goes out of scope (from this course's classes lesson), tying cleanup to that same mechanism means the cleanup cannot be forgotten -- it happens whether the function returns normally, returns early, or (in code that uses exceptions) an exception is thrown partway through.
This is a genuinely different mindset from C, where you (the programmer) are personally responsible for remembering to call free (or close a file, or unlock a lock) at every single place a function might exit -- miss one path, and you leak the resource. RAII moves that responsibility onto the type system: as long as the resource is owned by an RAII object with a correct destructor, leaving its scope by any path releases it, automatically, with nothing further for you to remember at each call site.
You'll see RAII again immediately in the next lesson, applied specifically to memory: std::unique_ptr and std::shared_ptr are RAII wrappers around a raw pointer, calling delete for you in their destructor -- but RAII itself is a general pattern, not something limited to smart pointers alone.
Guided lab
Predict: RAII-driven cleanup timing
Read this program and predict exactly what it prints, in order.
#include <iostream>
#include <string>
class ScopedLogger {
public:
ScopedLogger(std::string task) : task_(task) {
std::cout << "Starting: " << task_ << std::endl;
}
~ScopedLogger() {
std::cout << "Finished: " << task_ << std::endl;
}
private:
std::string task_;
};
void doWork() {
ScopedLogger logger("doWork");
std::cout << "Working..." << std::endl;
}
int main() {
std::cout << "Before doWork" << std::endl;
doWork();
std::cout << "After doWork" << std::endl;
return 0;
}Stuck? Get a hint.
Common mistakes
- Manually acquiring and releasing a resource with matched calls scattered through a function, instead of wrapping it in an RAII type that releases it automatically via its destructor.
- Assuming RAII only applies to memory -- it applies to any resource needing cleanup: files, locks, network connections, and more.
- Forgetting that RAII's automatic cleanup happens on every exit path from a scope, which is exactly why it's more reliable than remembering to clean up manually at each one.
Knowledge check
Takeaway
Wrap a resource's acquisition and release in a constructor/destructor pair, and cleanup becomes automatic on every exit path -- nothing left to remember at each call site.
Summary
RAII ties resource release to a destructor's automatic, guaranteed run at end of scope, making cleanup impossible to forget regardless of how a function exits.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.