Site

Memory — Pointers, References, RAII

Tutorial 9.0  •  C++ / Learn

9.0 What This Teaches

C++ gives direct access to memory. Understanding how memory works is essential for writing correct, efficient programs. This tutorial covers:

9.1 Stack and Heap

Every variable lives in one of two memory regions:
StackHeap
AllocationAutomatic on declarationExplicit with new
DeallocationAutomatic on scope exitExplicit with delete
SizeFixed at compile timeDynamic at runtime
SpeedVery fastSlower (allocator overhead)
Failure modeStack overflowMemory leak if delete forgotten
Prefer stack allocation. Use the heap (or smart pointers) only when you need dynamic size or lifetime that outlasts the current scope.

9.2 Pointers

int x = 10;
int* p = &x;      // p holds the address of x

std::cout << *p;   // dereference: read the value at the address
*p = 20;           // modify x through the pointer
std::cout << x;    // 20

int* null_p = nullptr;  // safer than NULL or 0
if (null_p != nullptr) {
    std::cout << *null_p;   // never reached
}
Always initialize pointers. Use nullptr rather than 0 or NULL for null pointers - it is type-safe.

9.3 References

int a = 5;
int& ref = a;   // ref is an alias for a; must be initialized; cannot be rebound

ref = 99;
std::cout << a;   // 99

// const reference: read-only alias; can bind to temporaries
const int& cref = 42;   // ok: 42 is kept alive by the const reference
References are simpler and safer than pointers for most use cases: they cannot be null, cannot be rebound, and do not need dereferencing syntax.

9.4 new and delete

int* p = new int(42);    // allocate one int on the heap
std::cout << *p;         // 42
delete p;                // free the memory
p = nullptr;             // avoid dangling pointer

int* arr = new int[10];  // allocate array
arr[0] = 1;
delete[] arr;            // free array - must match new[]
Every new must be paired with exactly one delete; every new[] with delete[]. In modern C++ prefer std::unique_ptr and std::vector to avoid manual management entirely.

9.5 RAII

class FileHandle {
public:
    FileHandle(const std::string& name) : name_(name) {
        std::cout << "opened: " << name_ << "\n";
    }
    ~FileHandle() {
        std::cout << "closed: " << name_ << "\n";
    }
private:
    std::string name_;
};

void demo() {
    FileHandle fh("log.txt");   // "opened: log.txt"
    // ... use fh ...
}   // "closed: log.txt" - destructor fires automatically, even if an exception is thrown
RAII (Resource Acquisition Is Initialization) is the C++ idiom for safe resource management. Acquire the resource in the constructor, release it in the destructor. The resource is always released when the object goes out of scope - even through exceptions.

9.6 Example - All Together

// Memory - stack vs heap, pointers, references, new/delete, RAII.

#include <iostream>
#include <string>

class FileHandle {
public:
    FileHandle(const std::string& name) : name_(name) {
        std::cout << "opened: " << name_ << "\n";
    }
    ~FileHandle() { std::cout << "closed: " << name_ << "\n"; }
private:
    std::string name_;
};

int main() {
    // pointers
    int x = 10;
    int* p = &x;
    *p = 20;
    std::cout << "x=" << x << "\n";

    // heap
    int* hp = new int(42);
    std::cout << "heap=" << *hp << "\n";
    delete hp;
    hp = nullptr;

    // references
    int a = 5;
    int& ref = a;
    ref = 99;
    std::cout << "a=" << a << "\n";

    // RAII
    {
        FileHandle fh("log.txt");
    }   // destructor fires here

    return 0;
}
x=20
heap=42
a=99
opened: log.txt
closed: log.txt

9.7 Exercise

Exercise
  • Allocate an array of 5 double values on the heap, fill it with squares (0.0, 1.0, 4.0, 9.0, 16.0), print them, then free the array correctly.
  • Write a function increment(int& n) that increments its argument. Call it and confirm the caller's variable changed.
  • Create an RAII class that prints "lock acquired" in its constructor and "lock released" in its destructor. Create an instance in a block and observe the output order.

9.8 Common Mistakes

Memory leak: forgetting delete

void leak() {
    int* p = new int(42);
    // ... forgot delete p; ...
}   // p goes out of scope; memory is never freed
Use std::unique_ptr instead of raw new.

Dangling pointer

int* p = new int(42);
delete p;
std::cout << *p;   // undefined behavior: p points to freed memory
Set pointers to nullptr after delete.

delete[] vs delete mismatch

int* arr = new int[10];
delete arr;    // undefined behavior: should be delete[]

9.9 Key Terms

TermMeaning
pointer (T*)Variable holding a memory address; dereference with *
reference (T&)Alias for an existing variable; cannot be null or rebound
nullptrNull pointer constant; type-safe replacement for NULL
new / deleteAllocate / free a single object on the heap
new[] / delete[]Allocate / free an array on the heap
dangling pointerPointer to freed or out-of-scope memory; dereferencing is undefined behavior
memory leakHeap memory allocated but never freed
RAIIResource Acquisition Is Initialization: destructor guarantees cleanup