Skip to content

← References, Pointers, Ownership step 2 of 4

Medium Primitives

A raw pointer owns nothing

Node* used to mean anything at all. It might be an array, a single object, something you must free, something you must not free, or nothing. Reading a 1990s header meant reading the comments and hoping.

Modern C++ narrowed it to one meaning, and the value of the convention is that it is now universal:

A raw pointer is a non-owning observer. Somebody else’s object, which may not be there.

You do not delete it. You do not store it past the lifetime of whatever owns it. You check it for null, because null is the reason it is a pointer and not a reference.

Pointer or reference?

T& T*
Can be null no yes
Can be reseated no yes
Owns the object no no

Both are non-owning. The reference is the stronger promise, so it is the default: use T& when the thing definitely exists, and T* when “not found” is one of the answers. A function that returns “the matching element, or nothing” returns a pointer for exactly that reason — there is no such thing as a null reference, and std::optional<T&> is not a thing you can write.

What owns things

Kind Owner
std::vector<T>, std::string the container
std::unique_ptr<T> the pointer, exclusively
std::shared_ptr<T> shared, by reference count
T*, T& nobody — an observer

If you find yourself writing delete on a T* parameter, the signature is wrong: it should be a unique_ptr so the transfer is visible at every call site.

The hazard that comes with observing

An observer stays valid only as long as the owner keeps the object where it is. This is the one to remember:

Node* p = &nodes[0];
nodes.push_back(...);   // may reallocate
p->value = 1;           // p points into freed memory

A std::vector that grows moves its elements. Every pointer, reference and iterator into it is invalidated. Track 5 gives this its own problem; for now, do not hold a pointer into a container across anything that could change the container’s size.

Your task

Node* find(std::vector<Node>& nodes, int id);

Return a pointer to the node with that id, or nullptr if there is none. apply is given: it looks each target up and adds delta to what it finds. Do not change it.

The starter returns a pointer, and every call reports a hit, and none of the nodes ever change. Work out what it is handing back.