CppRAII

RAII

RAII stands for Resource Acquisition Is Initialization. It is arguably the single most important idiom in all of C++: tie the lifetime of any resource — memory, a file handle, a network connection, a mutex lock — to the lifetime of an object. The object's constructor acquires the resource, and its destructor releases it. Because C++ guarantees that destructors run automatically when an object goes out of scope — even during exception unwinding — this makes resource cleanup essentially automatic and impossible to forget.
Why RAII matters so much

Every resource management bug covered earlier in this section — memory leaks, forgetting to close a file, forgetting to unlock a mutex — has the same shape: some cleanup step needs to run no matter how a block of code is exited, including early returns and thrown exceptions. Manually remembering to clean up on every single exit path is fragile. RAII sidesteps the whole problem: as long as the resource is owned by an object with a proper destructor, cleanup happens automatically, unconditionally, the instant that object goes out of scope.

RAII beyond memory

Resource

RAII wrapper

What the destructor does

Heap memory

std::unique_ptr / std::shared_ptr

Calls delete on the owned object

File handle

std::fstream / std::ifstream / std::ofstream

Closes the file

Mutex lock

std::lock_guard / std::unique_lock

Unlocks the mutex

Network connection

A custom RAII wrapper class

Closes the socket/connection

A worked example: a simple RAII file wrapper

Here is a minimal RAII class wrapping a C-style file handle. The constructor opens the file; the destructor closes it, no matter how the enclosing scope is exited.

raii_file_wrapper.cpp

CPP
#include <cstdio>
#include <stdexcept>
#include <iostream>

class FileHandle {
public:
    explicit FileHandle(const char* path) {
        file_ = std::fopen(path, "w");
        if (!file_) {
            throw std::runtime_error("Failed to open file");
        }
    }

    // Destructor: guaranteed to run when a FileHandle goes out of scope,
    // whether normally, via early return, or via an exception unwinding.
    ~FileHandle() {
        if (file_) {
            std::fclose(file_);
            std::cout << "File closed automatically" << std::endl;
        }
    }

    void write(const char* text) {
        std::fputs(text, file_);
    }

    // Prevent copying -- this simple wrapper only supports single ownership,
    // just like std::unique_ptr.
    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;

private:
    std::FILE* file_;
};

void writeLog() {
    FileHandle log("log.txt");   // resource acquired in the constructor
    log.write("Starting up...\n");

    // Even if code here throws an exception or returns early,
    // log's destructor still runs and the file still gets closed.

    log.write("Done.\n");
}   // log goes out of scope here -> file closed automatically
RAII in the standard library

You have already been using RAII throughout this tutorial without necessarily naming it. Three of the most common examples:

  • std::unique_ptr — acquires ownership of a heap object in its constructor, deletes it in its destructor.

  • std::lock_guard — locks a mutex in its constructor, unlocks it in its destructor, guaranteeing the lock is released even if an exception is thrown while it's held.

  • std::fstream (and std::ifstream / std::ofstream) — opens a file in its constructor, closes it in its destructor.

raii_stdlib_examples.cpp

CPP
#include <fstream>
#include <memory>
#include <mutex>

std::mutex mtx;

void example() {
    std::ofstream out("data.txt");   // RAII: file opens here
    out << "hello";
    // no need to call out.close() -- it happens automatically

    {
        std::lock_guard<std::mutex> lock(mtx);   // RAII: mutex locks here
        // ... critical section ...
    }   // mutex unlocks automatically here, even if an exception was thrown

    auto ptr = std::make_unique<int>(42);   // RAII: memory allocated here
}   // ptr's memory, and out's file, are both cleaned up automatically here
The big picture
RAII is why modern C++ code so rarely needs explicit cleanup calls like delete, fclose, or unlock. Instead of remembering to clean up, you simply hand resource ownership to an object designed to clean up after itself — and let scope do the rest. This single idea underlies nearly every “modern C++” recommendation in this entire memory management section.