Skip to content
  • Destructors & RAII · C++

Destructors & RAII

LearnViaNotes @learnvianotes· 2 min read

A destructor ~T() runs automatically when an object's lifetime ends. RAII ties a resource (memory, file, lock, socket) to an object so the destructor always releases it — even when an exception is thrown.

Destructor basics

C++
#include <iostream>
#include <string>

struct Noisy {
    std::string name;
    explicit Noisy(std::string n) : name(std::move(n)) { std::cout << "ctor " << name << '\n'; }
    ~Noisy() { std::cout << "dtor " << name << '\n'; }
};

int main() {
    Noisy a("a");
    {
        Noisy b("b");
        Noisy c("c");
    }                             // c then b destroyed here — reverse order
    Noisy d("d");
    std::cout << "end of main\n";
}                                 // d then a
Output
ctor a
ctor b
ctor c
dtor c
dtor b
ctor d
end of main
dtor d
dtor a

Writing an RAII wrapper

C++
#include <cstdio>
#include <iostream>
#include <stdexcept>

class File {
    std::FILE* f_;
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_); std::cout << "file closed\n"; }
    File(const File&) = delete;              // one owner only
    File& operator=(const File&) = delete;
    std::FILE* get() const { return f_; }
};

void process() {
    File out("raii_demo.txt", "w");
    std::fputs("hello\n", out.get());
    throw std::runtime_error("something failed");   // destructor still closes the file
}

int main() {
    try { process(); } catch (const std::exception& e) { std::cout << "caught: " << e.what() << '\n'; }
    std::remove("raii_demo.txt");
}
Output
file closed
caught: something failed

Scope guards

C++
#include <iostream>
#include <utility>

template <class F>
class ScopeExit {                       // run any cleanup when the scope ends
    F fn_;
public:
    explicit ScopeExit(F fn) : fn_(std::move(fn)) {}
    ~ScopeExit() { fn_(); }
    ScopeExit(const ScopeExit&) = delete;
    ScopeExit& operator=(const ScopeExit&) = delete;
};

int main() {
    int depth = 0;
    {
        ++depth;
        ScopeExit restore([&] { --depth; std::cout << "restored\n"; });
        std::cout << "depth " << depth << '\n';
    }
    std::cout << "depth " << depth << '\n';
}
Output
depth 1
restored
depth 0

Why doesn't C++ need a garbage collector?

When is a destructor called?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report