Intermediate · Classes, constructors and RAII · topic 15 of 42 · 25 min
RAII: let objects clean up for you
Explanation
What is a resource?
A resource is anything your program must give back after using it: heap memory, an open file, a network connection, a database connection, or a lock that stops two threads from clashing. Forgetting to give back a resource is a leak. Leaks pile up in long-running programs like servers until the machine runs out of memory or file handles.
The problem with manual cleanup
void saveReport() {
FILE* f = std::fopen("report.txt", "w");
if (!f) return;
if (!writeHeader(f)) return; // BUG: file never closed on this path
writeBody(f); // if this throws, the file is never closed
std::fclose(f);
}The RAII idea
RAII stands for "Resource Acquisition Is Initialisation". The name is clumsy but the idea is simple: wrap each resource in an object. The constructor acquires the resource; the destructor releases it. C++ guarantees that the destructor of a local object runs when the scope ends, whether the function returns normally, returns early, or an exception passes through. So cleanup happens automatically and exactly once.
#include <fstream>
#include <string>
void saveReport(const std::string& text) {
std::ofstream out("report.txt"); // constructor opens the file
if (!out) return;
out << "Monthly report\n";
out << text << '\n';
} // destructor closes the file, on every pathWriting your own RAII class
#include <cstdio>
#include <stdexcept>
class File {
public:
File(const char* path, const char* mode) : f_(std::fopen(path, mode)) {
if (!f_) throw std::runtime_error("cannot open file");
}
~File() { std::fclose(f_); }
File(const File&) = delete; // no copies: two owners would close twice
File& operator=(const File&) = delete;
std::FILE* get() const { return f_; }
private:
std::FILE* f_;
};
void writeLog() {
File log("app.log", "a");
std::fputs("started\n", log.get());
} // closed here automaticallyRAII is everywhere in the standard library
| Class | Resource it manages |
|---|---|
| std::string, std::vector, std::map | heap memory for their elements |
| std::ifstream, std::ofstream | an open file |
| std::unique_ptr, std::shared_ptr | a heap object |
| std::lock_guard, std::scoped_lock | a mutex lock |
| std::jthread (C++20) | a thread (joined automatically) |
This is why modern C++ code rarely contains delete, fclose or unlock. If you see a manual cleanup call, ask: "which RAII class should own this?"
Scope controls lifetime
void process() {
{
std::lock_guard<std::mutex> lock(m); // lock taken
shared.push_back(42);
} // lock released here
slowWorkThatNeedsNoLock();
}Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
RAII: let objects clean up for you · 1/6
RAII is the most important idea in C plus plus. It stops leaks of memory, files and locks.
Prefer a video by a teacher? Find free videos on “c++ RAII explained resource acquisition is initialization” on YouTube ↗
In real life: Database connections in a booking server
A ticket-booking server borrows a database connection from a pool for every request. If a request fails halfway with an exception and the connection is never returned, the pool slowly empties and the site stops responding during a Tatkal rush. Wrapping the borrowed connection in an RAII object returns it to the pool in the destructor, on every path.
class PooledConnection {
public:
explicit PooledConnection(Pool& pool) : pool_(pool), conn_(pool.acquire()) {}
~PooledConnection() { pool_.release(conn_); }
PooledConnection(const PooledConnection&) = delete;
PooledConnection& operator=(const PooledConnection&) = delete;
Connection& operator*() { return *conn_; }
private:
Pool& pool_;
Connection* conn_;
};
void bookSeat(Pool& pool, int seat) {
PooledConnection db(pool);
(*db).execute("UPDATE seats SET booked = 1 WHERE id = ?", seat); // may throw
} // connection returned to the pool, even if execute threwPractice (0/4 done)
What does RAII guarantee?
Which of these is NOT an RAII type?
Why does the File RAII class delete its copy constructor?
Write an RAII class ScopedMessage that prints "BEGIN <name>" when created and "END <name>" when destroyed. Use it in a function that has an early return and in one that throws an exception (catch it in main). Check that END is always printed.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Copying objects: the rule of three, five and zeroClasses, constructors and RAII · same module
- Classes and objects: data plus behaviourClasses, constructors and RAII · same module
- Smart pointers 1: std::unique_ptrTemplates, smart pointers and move semantics
- Exceptions: throw, try, catch and noexceptExceptions, concurrency and building projects with CMake
- try-with-resources and clean-upJava Programming (Core to Spring Boot) · Exceptions and generics
- C++ interview questions and how to answer themCompetitive programming, interviews and careers
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does the R in RAII stand for?
Answer: Resource
Resource Acquisition Is Initialisation.
An exception is thrown inside a function. What happens to the local objects already created there?
Answer: Their destructors run as the exception leaves the scope (stack unwinding)
Stack unwinding destroys local objects in reverse order, which is what makes RAII reliable.
Which is the RAII way to hold a mutex lock?
Answer: std::lock_guard<std::mutex> g(m);
lock_guard locks in its constructor and unlocks in its destructor.
Why should you avoid creating an RAII object with new?
Answer: Its destructor only runs when you delete it, so the automatic cleanup is lost
RAII relies on automatic lifetime. A heap object lives until someone deletes it.
How do you pass ownership of a unique_ptr into a function sink(std::unique_ptr<T> p)?Related
When does a local object's destructor run?Related
Answer: When the object goes out of scope
Local objects are destroyed automatically at the end of their scope.
Which pair makes a deep copy automatically when you copy the object?Related
Answer: A std::vector member
std::vector copies all its elements into new memory.
Which base class should your own exception types usually derive from?Related
Answer: std::exception (often via std::runtime_error)
Then generic handlers for std::exception catch them too, and what() works.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 1 more topic in “Classes, constructors and RAII” before its exam: Copying objects: the rule of three, five and zero.
- Module 4 exam: Classes, constructors and RAII · 12 questions · 20 min, without help. 4 topics of this module still to finish first.
- Then the next module, Operator overloading, inheritance and polymorphism. 42 topics left in the course before the final exam.