Skip to content

RAII and Resource Management

RAII—Resource Acquisition Is Initialization—binds a resource's lifetime to an object. Construction establishes ownership and destruction releases it. This applies to memory, files, sockets, locks, transactions, and other resources.

Deterministic cleanup

Automatic objects are destroyed when their scope exits, including during stack unwinding after an exception. Member and base destructors then release their resources in reverse construction order.

std::string first_line(std::filesystem::path const& path) {
    std::ifstream input{path};
    if (!input) throw std::runtime_error{"cannot open input file"};

    std::string line;
    if (!std::getline(input, line)) {
        throw std::runtime_error{"cannot read first line"};
    }
    return line;
} // input closes here on every exit path

No explicit close() path is required for correctness. Destructors used for cleanup should not allow exceptions to escape during stack unwinding.

Ownership tools

  • direct values and standard containers: preferred default;
  • std::unique_ptr<T>: exclusive dynamic ownership;
  • std::shared_ptr<T>: reference-counted shared ownership;
  • std::weak_ptr<T>: non-owning observation of shared state and cycle breaking;
  • raw pointers/references: normally non-owning access when their lifetime contract is clear.

Prefer std::make_unique and std::make_shared where their construction and allocation semantics fit. Shared ownership has atomic reference-count overhead and does not make the pointed-to object thread-safe.

Locks are resources

std::mutex mutex;
std::vector<int> values;

void append(int value) {
    std::lock_guard guard{mutex};
    values.push_back(value);
}

The guard unlocks during every exit path. std::scoped_lock can acquire multiple mutexes using a deadlock-avoidance mechanism, though the larger locking protocol still needs design.

Custom resource handle

A resource wrapper should acquire or receive one resource, expose only valid operations, release in its destructor, and define copy/move semantics explicitly. Often it is movable but not copyable.

RAII versus garbage collection

Garbage collection reclaims unreachable memory according to a runtime policy. RAII ends resource ownership at deterministic scope/object-lifetime boundaries. C++ can use tracing collectors in specialized environments, and Java can use scope-based resource wrappers; the concepts are not mutually exclusive.

Exercises

  1. Wrap a C file handle in a movable RAII class.
  2. Explain why a cycle of shared_ptr owners leaks without a weak edge.
  3. Describe why explicit lock() followed by unlock() is exception-unsafe.