Lesson 21 • Advanced
RAII Architecture
Apply Resource Acquisition Is Initialization to files, locks, sockets, and transactions — guaranteeing cleanup even when exceptions strike.
What You'll Learn
- ✓ The RAII principle and why it matters
- ✓ RAII wrappers for files and handles
- ✓ lock_guard and RAII mutex management
- ✓ Scope guards for transactional rollbacks
What is RAII?
RAII ties resource lifetime to object lifetime. The constructor acquires the resource (file, lock, memory, connection). The destructor releases it. Since C++ guarantees destructors run when objects leave scope — even during stack unwinding from exceptions — RAII makes resource leaks structurally impossible.
Think of RAII like a hotel key card. When you check in (construct), you get access. When you check out (destruct), access is revoked. You can't forget to return the key — checkout handles it automatically.
RAII File Guard
Build a file wrapper that guarantees closure even with exceptions
#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>
using namespace std;
// RAII wrapper for file handles
class FileGuard {
ofstream file;
string filename;
public:
FileGuard(const string& name) : filename(name) {
file.open(name);
if (!file.is_open()) {
throw runtime_error("Cannot open file: " + name);
}
cout << "Opened: " << filename << endl;
}
~FileGuard() {
if (file.is_open()) {
...RAII Mutex Locking
Use lock_guard for exception-safe thread synchronization
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
using namespace std;
// RAII mutex wrapper (simplified lock_guard)
class MutexGuard {
mutex& mtx;
public:
MutexGuard(mutex& m) : mtx(m) {
mtx.lock();
}
~MutexGuard() {
mtx.unlock(); // Always unlocks, even on exception
}
MutexGuard(const MutexGuard&) = delete;
MutexGuard& operator=(const MutexGuard&) = delete;
};
// Thread-safe counter using RAII
class SafeCounter {
int cou
...Scope Guard Pattern
Build a generic scope guard for transactional rollbacks
#include <iostream>
#include <functional>
#include <vector>
using namespace std;
// Generic scope guard — runs cleanup on scope exit
class ScopeGuard {
function<void()> cleanup;
bool active = true;
public:
ScopeGuard(function<void()> fn) : cleanup(move(fn)) {}
~ScopeGuard() {
if (active) cleanup();
}
// Dismiss — prevent cleanup (e.g., on success)
void dismiss() { active = false; }
ScopeGuard(const ScopeGuard&) = delete;
ScopeGuard& ope
...Common Mistakes
⚠️ Creating unnamed temporaries: lock_guard<mutex>(mtx); creates and immediately destroys the guard. Use lock_guard<mutex> lock(mtx);.
⚠️ Allowing copies of unique resources: File handles, locks, and sockets should delete copy constructors and only allow move.
⚠️ Destructor exceptions: Never throw from a destructor — it causes std::terminate during stack unwinding.
Pro Tips
💡 RAII everywhere: Wrap every C API resource (FILE*, HANDLE, socket fd) in an RAII class. Never call close/free manually.
💡 Use scoped_lock (C++17): scoped_lock locks multiple mutexes simultaneously without deadlock risk.
💡 Scope guard libraries: Boost.ScopeExit and GSL's finally provide battle-tested scope guard implementations.
📋 Quick Reference
| RAII Type | Resource |
|---|---|
unique_ptr | Heap memory |
lock_guard | Mutex lock |
fstream | File handle |
unique_lock | Mutex (deferrable) |
scoped_lock | Multiple mutexes |
Lesson Complete!
You've mastered RAII — the most important C++ idiom for safe resource management. Next: Constexpr & Compile-Time Programming — moving computation from runtime to compile time.
Sign up for free to track which lessons you've completed and get learning reminders.