C++

Constructors, Destructors & RAII

Classes & Objects introduced the constructor briefly, as the method that runs automatically on creation. This chapter is the full picture: what runs at the other end of an object's life too, and the single idiom — RAII — that both enable, which quietly eliminates an entire category of bug that's constant, manual work in C.

Quick reference
What it is
A constructor runs automatically when an object is created; a destructor runs automatically when it's destroyed. RAII (Resource Acquisition Is Initialization) is the pattern of tying a resource's lifetime to an object's, using exactly these two.
Mental model
A constructor is move-in day — the object sets itself up. A destructor is move-out day — it happens automatically, even if you "forget," the moment the object's scope ends.
Key numbers
Every object gets exactly one destructor call, guaranteed, when it goes out of scope — no matter how many exit paths (early return, exception) the function has.
Reach for it when
An object owns a resource — memory, a file handle, a lock, a hardware peripheral — that must be released exactly once, no matter how the function exits.

Constructors, in more depth

class Buffer {
public:
    Buffer(int size) {           // constructor — takes arguments like any function
        data = new int[size];     // allocate on construction
        capacity = size;
        std::cout << "Buffer created\n";
    }

private:
    int *data;
    int capacity;
};

Buffer b(100);   // constructor runs immediately — data is allocated before the next line runs

A constructor can take parameters exactly like any function, letting an object configure itself at creation time instead of needing a separate, easy-to-forget "init" call — a pattern C code has to write by hand every time.

Destructors

class Buffer {
public:
    Buffer(int size) {
        data = new int[size];
        std::cout << "Buffer created\n";
    }

    ~Buffer() {                    // destructor — same name as the class, prefixed with ~
        delete[] data;              // free exactly what the constructor allocated
        std::cout << "Buffer destroyed\n";
    }

private:
    int *data;
};

void useBuffer() {
    Buffer b(100);
    // ... use b ...
}   // b goes out of scope HERE — ~Buffer() runs automatically, data is freed

The destructor runs the instant an object's scope ends — no explicit call needed, and no way to accidentally skip it on a normal exit path. This is the direct C++ answer to the exact Dynamic Memory problem of a forgotten free(): pair every resource's acquisition with a matching release, in one place, guaranteed to run.

RAII — the idiom both enable

RAII (Resource Acquisition Is Initialization) is the practice of acquiring a resource in a constructor and releasing it in the matching destructor — tying the resource's lifetime directly to an object's lifetime, which C++ already guarantees is well-defined and automatic:

class FileHandle {
public:
    FileHandle(const char *path) {
        file = fopen(path, "r");   // ACQUIRE in the constructor
    }
    ~FileHandle() {
        if (file) fclose(file);     // RELEASE in the destructor — always runs
    }
    FILE *get() { return file; }

private:
    FILE *file;
};

void readConfig() {
    FileHandle f("config.txt");   // file opened
    // ... use f.get() ...
    // even if an exception is thrown here, ~FileHandle() STILL runs
}   // file closed automatically, guaranteed, right here

Compare this to the equivalent C file-handling code, where every single exit path — including an early return on an error — has to remember its own matching fclose. RAII makes "forgetting to clean up" a non-issue by construction, not a discipline problem.

An object's full lifecycle

An object's lifecycle: construction runs once at creation, the object is used for a while, and destruction runs automatically and exactly once when it goes out of scope Constructor runs once, on creation Object in use any number of method calls Destructor runs once, automatically Guaranteed exactly once each — regardless of how many code paths lead to the end of scope
In C, the equivalent of the last box has to be written explicitly on every exit path by hand — here it's structural, not a habit to remember.

Real-world applications

Gotchas

Field notes