A pointer can hold an address or nullptr and can be reseated. A reference aliases an existing object and normally expresses required non-owning access. Neither raw form automatically explains who destroys an object.
Use std::unique_ptr for exclusive ownership and std::shared_ptr only when lifetime is genuinely shared. A std::weak_ptr observes shared ownership without extending it and can break reference cycles.
unique_ptr cannot be copied. std::move transfers its owned pointer and leaves the source empty, making the lifetime handoff visible.
#include <iostream>
#include <memory>
#include <string>
#include <utility>
struct Job { std::string name; };
void run(const Job& job) { std::cout << job.name << '\n'; }
int main() {
auto owner = std::make_unique<Job>(Job{"backup"});
run(*owner);
auto next_owner = std::move(owner);
std::cout << std::boolalpha << (owner == nullptr) << '\n';
}
backup
true
A pointer, reference, iterator, or string_view dangles when its source object is destroyed or relocated. Never return a reference to a local variable, and re-check container invalidation rules after mutation.
A pointer stores an address; it does not automatically describe who owns the object or how long that object remains alive. Most pointer defects are lifetime defects: dereferencing null, keeping an address after the object is destroyed, deleting the same allocation twice, or leaking an allocation when control leaves early.
Express ownership in the type. Use a value for ordinary ownership, a reference for a required non-null alias, unique_ptr for one transferable owner, shared_ptr only for genuine shared lifetime, and weak_ptr for a non-owning observation of shared state. A raw pointer is appropriate for nullable, non-owning access when that contract is clear.
#include <iostream>
#include <memory>
#include <string>
struct User {
std::string name;
};
void printUser(const User* user) {
if (user == nullptr) {
std::cout << "No user\n";
return;
}
std::cout << user->name << '\n';
}
int main() {
auto owner = std::make_unique<User>(User{"Mina"});
printUser(owner.get());
}
unique_ptr owns the User. The raw pointer returned by get() is only a temporary observer and must not be deleted.
<code>std::unique_ptr</code> represents exclusive ownership, so copying would create two owners that both try to delete the same object. Transfer ownership with <code>std::move</code>, pass a raw pointer or reference for non-owning access, and use <code>std::shared_ptr</code> only when ownership genuinely must be shared.
If two objects hold owning <code>shared_ptr</code> references to each other, neither reference count reaches zero. Break the cycle by making the back-reference a <code>std::weak_ptr</code>, then call <code>lock()</code> before access.
A pointer communicates that null may be meaningful or that pointer-style traversal is required. A reference communicates that a valid object is required and cannot be reseated inside the function.
Explore 500+ free tutorials across 20+ languages and frameworks.