RAII (Resource Acquisition Is Initialization)

RAII (Resource Acquisition Is Initialization) is a C++ idiom used to manage resources (memory, file handles, sockets, etc.) in a safe and efficient way.

💡 Key Idea

  • Tie resource lifetime to an object’s lifetime.
  • The resource is acquired in the constructor and released in the destructor.
  • This ensures automatic cleanup of resources, preventing memory leaks and resource leaks.

🔹 Why Use RAII?

Automatic resource management – No need for manual new/delete calls.
Exception safety – Resources are cleaned up when an exception occurs.
Encapsulation – Resource management logic is hidden inside a class.
Prevents memory leaks – Guarantees that allocated resources are freed.


🔹 Example 1: RAII for Memory Management

Instead of using raw pointers, use smart pointers like std::unique_ptr.

❌ Without RAII (Memory Leak Risk)

void riskyFunction() {
    int* data = new int[100];  // Allocating memory

    if (some_condition) {
        throw std::runtime_error("Error occurred!");
    }
    
    delete[] data; // This may never execute if an exception is thrown!
}

🚨 Problem: If an exception occurs, delete[] data is never called, causing a memory leak.


✅ With RAII (Safe Memory Management)

#include <iostream>
#include <memory>

void safeFunction() {
    std::unique_ptr<int[]> data = std::make_unique<int[]>(100);  // RAII

    if (some_condition) {
        throw std::runtime_error("Error occurred!");  // No memory leak
    }
} // `data` is automatically deleted when it goes out of scope

✔️ Safe: Memory is automatically deallocated when data goes out of scope.


🔹 Example 2: RAII for File Handling

Instead of manually handling file opening/closing, use RAII with std::fstream.

❌ Without RAII (Risk of Unclosed Files)

#include <fstream>

void writeFile() {
    std::ofstream file("example.txt");  // Open file
    if (!file.is_open()) return;

    file << "Hello, RAII!";

    // If function exits early (exception/return), file remains open!
}

🚨 Problem: If an exception is thrown, the file might not be properly closed.


✅ With RAII (Automatic File Closing)

#include <fstream>
#include <iostream>

void writeFile() {
    std::ofstream file("example.txt");  // RAII: File closes automatically when `file` goes out of scope

    if (!file) {
        std::cerr << "Failed to open file!\n";
        return;
    }

    file << "Hello, RAII!";
} // File is automatically closed here

✔️ Safe: File is automatically closed when file goes out of scope.


🔹 Example 3: Custom RAII Wrapper for File Handling

You can create a custom RAII class to manage file resources.

#include <iostream>
#include <cstdio>  // For FILE*

class FileWrapper {
private:
    FILE* file;
public:
    explicit FileWrapper(const char* filename, const char* mode) {
        file = std::fopen(filename, mode);
        if (!file) {
            throw std::runtime_error("Failed to open file");
        }
    }

    ~FileWrapper() {
        if (file) {
            std::fclose(file);
            std::cout << "File closed\n";
        }
    }

    FILE* get() { return file; } // Provide access if needed
};

int main() {
    try {
        FileWrapper file("example.txt", "w");
        std::fprintf(file.get(), "Hello RAII!");
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << "\n";
    }
} // `file` is automatically closed here

✔️ Safe: File is automatically closed in the destructor.


🔹 RAII with Mutex (Thread Safety)

RAII is commonly used with mutexes to avoid deadlocks.

❌ Without RAII (Risk of Deadlock)

#include <iostream>
#include <mutex>

std::mutex mtx;

void unsafeFunction() {
    mtx.lock();  // Manually locking the mutex
    if (some_condition) return;  // Forgot to unlock the mutex! (Deadlock risk)

    mtx.unlock();
}

🚨 Problem: If the function exits early, the mutex remains locked.


✅ With RAII (Automatic Unlocking)

Use std::lock_guard to ensure automatic unlocking.

#include <iostream>
#include <mutex>

std::mutex mtx;

void safeFunction() {
    std::lock_guard<std::mutex> lock(mtx);  // RAII: Mutex will be unlocked automatically
    std::cout << "Mutex locked safely\n";
} // Mutex automatically unlocked here

✔️ Safe: std::lock_guard ensures the mutex is always released.


🔹 RAII and Smart Pointers

Modern C++ uses RAII extensively through smart pointers:

Smart PointerPurpose
std::unique_ptrExclusive ownership of resources
std::shared_ptrShared ownership (reference counting)
std::weak_ptrNon-owning reference to shared_ptr

Example with std::unique_ptr:

#include <iostream>
#include <memory>

class Resource {
public:
    Resource() { std::cout << "Resource acquired\n"; }
    ~Resource() { std::cout << "Resource released\n"; }
};

void useResource() {
    std::unique_ptr<Resource> res = std::make_unique<Resource>();  // RAII
} // `res` is automatically deleted here

int main() {
    useResource();
} // Output: Resource acquired → Resource released

✔️ Safe: No need to manually delete the resource.


🔹 Summary

RAII ties resource management to object lifetime (constructor acquires, destructor releases).
✅ Prevents memory leaks, file leaks, and deadlocks.
✅ Works well with smart pointers, mutexes, file handling, and other resources.
✅ Encourages exception safety by ensuring proper cleanup.
✅ Used extensively in modern C++ and recommended over manual resource management.