Tutorials Logic, IN info@tutorialslogic.com

C++ Pointers, References, and Smart-Pointer Ownership

Addresses Are Not Ownership

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.

Transfer Exclusive Ownership

unique_ptr cannot be copied. std::move transfers its owned pointer and leaves the source empty, making the lifetime handoff visible.

Create and move a unique_ptr

Create and move a unique_ptr
#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';
}
Output
backup
true

Recognize Dangling Access

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.

  • Prefer make_unique and make_shared.
  • Pass T& or const T& when no ownership transfer occurs.
  • Check nullable pointers before dereference.

Ownership, Lifetime, and Nullable Access

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.

  • Never return a pointer or reference to a local variable.
  • Prefer std::make_unique and std::make_shared instead of spelling new directly.
  • Move a unique_ptr to transfer ownership; copying it is intentionally disabled.
  • Use weak_ptr to observe shared objects without creating ownership cycles.
  • Check a nullable pointer before dereferencing it, and keep the pointee alive for the whole access.

Separate ownership from observation

Separate ownership from observation
#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.

Before you move on

Ownership Review

5 checks
  • Every allocation has one visible owner.
  • Shared ownership is justified.
  • Raw access cannot outlive its owner.
  • Moves leave a valid state.
  • Cycles use a non-owning edge.

Pointer Failure Boundary

  • Dangling dereference

    Setting one pointer to nullptr does not repair aliases to a destroyed object. Prefer references or smart pointers that express lifetime and ownership.

Ownership Questions

<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.

Next Step
Next Practice

Finish the concept here, then reinforce it with hands-on coding, interview prep, or a tool that matches the topic.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.