Constructors, Destructors & RAII

Course 1 · Ch 5
Constructors, Destructors & RAII
C++'s own real answer to C's central safety tradeoff — invented roughly fifteen years before Rust's Drop

c2-2 was the C track's own central chapter: manual malloc/free, with every allocation carrying real risk of a forgotten free. This chapter is C++'s answer — and, genuinely, it's the same answer Rust would later give.

RAII — Resource Acquisition Is Initialization

The core idea: tie a resource's lifetime directly to an object's lifetime. Acquire the resource in the constructor; release it in the destructor. When the object goes out of scope — through any exit path, including an early return or an exception — the destructor runs automatically, releasing the resource with zero remaining programmer effort at that point.

A Worked RAII Example — A Simple Owning Wrapper

class IntArray { private: int *data; public: IntArray(int size) { data = new int[size]; // acquire — in the constructor } ~IntArray() { delete[] data; // release — in the destructor, always } };

Compare this directly against c2-2's own discipline: "every allocation needs a matching free, and the programmer must remember every single time." Here, allocate once in the constructor, and never have to remember to free again — the destructor handles it unconditionally, even during an exception unwind.

The Direct Rust Parallel

This is, genuinely, exactly Rust's Drop trait — invented roughly fifteen years earlier, via manual class-writing instead of a language-level trait system. Rust's own ownership model (rust1-3) and smart pointers (rust2-2) were historically compared to, and influenced by, this exact C++ idiom. A real, rare moment of genuine convergent evolution between the two languages, unlike most of the C track's own contrasts.

new and delete

C++'s own heap-allocation operators — a real, concrete improvement over C's malloc/free. new allocates and calls the constructor automatically; delete calls the destructor and frees the memory. Raw malloc/free know nothing about object lifecycles at all — just bytes.

Circle *c = new Circle(5.0); // allocates AND constructs delete c; // destructs AND frees int *arr = new int[10]; // array form — needs the matching array form below delete[] arr;

Mismatching them — new with delete[], or vice versa — is undefined behavior, the exact same category c3-4 covered in full.

The Rule of Three, Previewed

A class managing a resource manually generally needs a destructor, a copy constructor, and a copy assignment operator together — or copies of the object risk a double-free exactly like c2-3's own bug catalog. Full depth arrives in cpp2-8; this is just the forward pointer.

Exception Safety, Briefly

RAII's other major payoff, previewed ahead of cpp2-6's own Exception Handling chapter: because destructors run on any scope exit — including an exception unwinding through the call stack — RAII-managed resources are cleaned up automatically even when an error is thrown mid-function. C's manual cleanup pattern has no equivalent mechanism at all; C has no exceptions and no automatic stack unwinding.

ConceptC (c2-2)C++ RAIIRust
Resource releasemanual free() — programmer must rememberautomatic — destructor on scope exitautomatic — Drop on scope exit
Cleanup on an error mid-functionno mechanism — manual onlyautomatic — destructors run during unwindingautomatic — Drop runs even on panic unwind
Allocation + initializationtwo separate manual steps (malloc, then init)one step — new calls the constructorone step — the constructor pattern is the norm
Prefer ready-made RAII wrappers over hand-rolled ones
This chapter's IntArray is a teaching device. Course 2's smart pointers (unique_ptr/shared_ptr) are the standard, battle-tested RAII wrappers real code should reach for — reinventing them by hand is rarely the right call outside a learning exercise.
A constructor that throws partway through skips that object's destructor
If a constructor throws before finishing, the object was never fully constructed — its destructor does not run for it, since construction never completed. A genuine, subtle edge case worth knowing exists, covered properly once cpp2-6 introduces exceptions in full.

Coding Challenges

Challenge 1

Write a class that acquires a resource (e.g. allocates an int array with new[]) in its constructor and releases it with delete[] in its destructor. Create an instance inside a nested scope and print a message from both the constructor and destructor to observe the order they run in.

📄 View solution
Challenge 2

Allocate an object with new, then intentionally never call delete on it. Explain, referencing c2-2's own material, what class of bug this is and why RAII alone doesn't protect against this specific mistake.

📄 View solution
Challenge 3

Explain precisely why RAII is considered the same underlying idea as Rust's Drop trait, and name the one concrete mechanical difference between how each language actually triggers the cleanup code.

📄 View solution

Chapter 5 Quick Reference

  • RAII — acquire in the constructor, release in the destructor, tied to object lifetime
  • new/delete — allocate+construct / destruct+free in one step, unlike raw malloc/free
  • new[] must be matched with delete[] — mismatching is undefined behavior
  • Destructors run on any scope exit, including exception unwinding — C has no equivalent mechanism at all
  • This is genuinely the same idea as Rust's Drop, roughly fifteen years earlier
  • Next chapter: operator overloading — giving custom types +, ==, and <<