Skills101

C++ Programming · Lesson 8 of 9 · Updated ·

Memory in C++ — new/delete, RAII, Smart Pointers, Copy vs Move and Exceptions

In C you malloc and free by hand. Modern C++ makes objects clean up after themselves, so leaks and double frees mostly disappear.

1. new and delete

int *p = new int(42);        // one int on the heap
int *arr = new int[100];     // an array
delete p;                    // free one
delete[] arr;                // free an array (note the [])

Forgetting delete leaks memory; using delete instead of delete[] is undefined behaviour. In modern C++ you should rarely write new/delete yourself.

2. RAII: tie resources to object lifetime

Inside a scope, a unique_ptr owns a Node object on the heap; when the scope ends, its destructor frees the Node automatically

RAII (Resource Acquisition Is Initialisation): acquire a resource in a constructor, release it in the destructor. Since destructors run automatically — even when an exception is thrown — the resource is always released. string, vector, fstream, lock_guard and smart pointers all work this way.

3. Smart pointers

#include <iostream>
#include <memory>
#include <string>
using namespace std;

struct Node {
    string name;
    explicit Node(string n) : name(n) { cout << "create " << name << "\n"; }
    ~Node() { cout << "destroy " << name << "\n"; }
};

int main() {
    {
        unique_ptr<Node> a = make_unique<Node>("A");     // single owner
        unique_ptr<Node> b = move(a);                     // ownership moves to b
        cout << (a == nullptr) << " " << b->name << "\n";
    }                                                     // b goes out of scope → A destroyed
    shared_ptr<Node> s1 = make_shared<Node>("S");
    {
        shared_ptr<Node> s2 = s1;                         // two owners
        cout << s1.use_count() << "\n";
    }
    cout << s1.use_count() << "\n";
    return 0;
}                                                         // last owner gone → S destroyed
create A
1 A
destroy A
create S
2
1
destroy S
Smart pointerOwnersUse for
unique_ptrexactly one (can be moved, not copied)the default choice
shared_ptrmany (reference counted)genuinely shared ownership
weak_ptrobserves a shared_ptr without owningbreaking reference cycles

4. Copy vs move, and the rule of three/five

Copying a big object (a vector with a million items) is expensive. C++11 added move: transfer the internals instead of copying them.

#include <iostream>
#include <vector>
#include <utility>
using namespace std;

int main() {
    vector<int> big(1000000, 7);
    vector<int> copyOf = big;              // copies a million ints
    vector<int> movedTo = move(big);       // steals the buffer — very fast
    cout << copyOf.size() << " " << movedTo.size() << " " << big.size() << "\n";
    return 0;
}
1000000 1000000 0

(After a move, the source is valid but unspecified — usually empty. Don't rely on its contents.)

Rule of three / five: if your class manages a raw resource and needs a custom destructor, it probably also needs a custom copy constructor and copy assignment (three), plus move constructor and move assignment (five). Rule of zero: better still, use vector, string and smart pointers so you need none of them.

5. Exceptions

#include <iostream>
#include <stdexcept>
using namespace std;

double divide(double a, double b) {
    if (b == 0) throw invalid_argument("division by zero");
    return a / b;
}

int main() {
    try {
        cout << divide(10, 4) << "\n";
        cout << divide(1, 0) << "\n";
        cout << "not reached\n";
    } catch (const invalid_argument &e) {
        cout << "Error: " << e.what() << "\n";
    }
    cout << "continues\n";
    return 0;
}
2.5
Error: division by zero
continues

Catch by const reference. Standard exceptions include invalid_argument, out_of_range (e.g. vector::at), runtime_error, bad_alloc.

Common mistakes

  • Raw new without a matching delete (use make_unique instead).
  • Copying unique_ptr (compile error — use move).
  • shared_ptr cycles (A owns B, B owns A) → never freed; use weak_ptr for one side.

Quick recap

  • Avoid raw new/delete; RAII cleans up in destructors.
  • unique_ptr (default), shared_ptr (shared), weak_ptr (no ownership).
  • Move transfers resources cheaply; follow the rule of zero.
  • throw / try / catch (by const reference) for errors.

Practice what you learned

📝 Quiz: Memory and Smart Pointers5 questions · instant answers & explanations · +10 XP · counts towards your free certificateTake the quiz →