Modern Memory Management

Modern Memory Management

Replace manual ownership with RAII and smart pointers.

Modern Memory Management

Old style to avoid

Widget* widget = new Widget();
// ...
delete widget;

This style is error-prone because exceptions, multiple return paths, or missed deletes can leak memory.

Preferred style

auto widget = std::make_unique<Widget>();

The object is automatically destroyed when the owning smart pointer goes out of scope.

That is the normal modern path: prefer stack allocation first, then std::unique_ptr when the object truly needs dynamic lifetime.

Shared ownership

Use std::shared_ptr only when several objects truly co-own the same resource.

auto config = std::make_shared<Config>();
start_worker(config);
start_logger(config);

This is appropriate when multiple long-lived parts of the program must keep the same object alive independently.

Rule of thumb

Ownership transfer example

std::unique_ptr<Widget> make_widget() {
	return std::make_unique<Widget>();
}

void install(std::unique_ptr<Widget> widget) {
	current_widget = std::move(widget);
}

Passing std::unique_ptr by value is a clean way to express ownership transfer.

Shared ownership pitfalls

Shared ownership can hide lifetime relationships. It also introduces atomic reference counting overhead and can create cycles.

struct Node {
	std::shared_ptr<Node> next;
	std::weak_ptr<Node> prev;
};

Use std::weak_ptr to break cycles in graph-like structures.

Why the weak pointer matters

If both directions used std::shared_ptr, two nodes could keep each other alive forever even after the rest of the program released them.

struct Person {
	std::string name;
	std::shared_ptr<Person> partner;
};

This kind of cycle leaks until one side becomes std::weak_ptr.

struct Person {
	std::string name;
	std::weak_ptr<Person> partner;
};

Views and borrowing

Owning types manage lifetime. Borrowing types such as std::span and std::string_view only observe data. This distinction is one of the most important modern C++ habits to internalize.

void draw(std::span<const Pixel> pixels);
void greet(std::string_view name);

Borrowing APIs are powerful, but the caller must keep the underlying data alive for the duration of the use.

Exception safety

RAII is what makes exception-safe code ordinary rather than special. Acquire resources into objects immediately so cleanup always happens on every path.

Practical checklist

Example in practice

#include <memory>

int main() {
    auto value = std::make_unique<int>(42);
    return *value;
}

Try this variation

Transfer ownership out of one scope and into another with `std::move`. It is the fastest way to see what unique ownership really means.

#include <memory>

std::unique_ptr<int> make_value() {
    auto value = std::make_unique<int>(42);
    return value;
}