C++ PROGRAMMING ยท 14 MIN STUDY

Pointer ownership and resource management

Learn the difference between owning and non-owning pointers, and choose a clear ownership model instead of manually guessing who should delete an object.

Learning objectives

  • Explain ownership as responsibility for an object's lifetime.
  • Distinguish an owning handle from a non-owning pointer.
  • Use unique_ptr for exclusive ownership and understand shared ownership trade-offs.
  • Recognize double deletion, leaks, and use-after-free errors.

Prerequisites: pointers, references, object lifetime, and basic class usage. Review Pointers and memory addresses, References and const references, and Object lifetime and dangling references first.

What ownership means

Ownership answers a practical question: which object or component is responsible for keeping a resource alive and releasing it? A raw pointer such as int* describes an address; by itself, it does not say whether the pointer owns the object.

int value{10};
int* view = &value; // non-owning pointer: does not delete value

The integer's lifetime is managed by its automatic scope. Deleting view would be incorrect because it does not point to a separately allocated object owned by this pointer.

Exclusive ownership with unique_ptr

std::unique_ptr represents one exclusive owner. It cannot be copied, but ownership can be transferred with a move.

C++14 example: make_unique was added in C++14. Include both headers because std::move is declared in <utility>.

#include <memory>
#include <utility>

int main() {
    auto first = std::make_unique<int>(25);
    // auto second = first; // ERROR: unique_ptr cannot be copied
    auto second = std::move(first);
    // first is empty; second owns the integer
}

When the owning unique_ptr is destroyed or reset, it destroys the object it owns. This prevents many manual delete mistakes.

Shared ownership with shared_ptr

std::shared_ptr allows multiple smart pointers to share ownership. The managed object is destroyed when the final shared owner releases it.

#include <memory>

int main() {
    auto owner_a = std::make_shared<int>(50);
    {
        auto owner_b = owner_a; // shares ownership
    } // owner_b releases one share
    // owner_a still keeps the int alive
}

Shared ownership has bookkeeping cost and can create cycles. Do not choose it just because copying a pointer seems convenient. Use it only when multiple components genuinely share lifetime responsibility.

Why ownership cycles matter

If object A owns B through a shared_ptr and B owns A through another shared_ptr, both reference counts may remain nonzero after the rest of the program releases them. The cycle keeps both objects alive. A std::weak_ptr is a non-owning observer that can refer to an object managed by shared ownership without increasing its strong ownership count.

Resource leaks and double deletion

int* data = new int{9};
delete data;
// delete data; // WRONG: second delete is undefined behavior

Manual allocation creates a responsibility to release exactly once, and to do so on every control path. Exceptions and early returns make this difficult. RAII wraps the resource in an object whose destructor releases it automatically. Prefer std::vector, std::string, file-stream objects, and smart pointers over direct new/delete in ordinary application code.

Ownership design checklist

  • Use a value or standard container when it naturally owns the data.
  • Use unique_ptr when one component owns a dynamically allocated object.
  • Use shared_ptr only for genuinely shared lifetime.
  • Use raw pointers or references as non-owning views when nullability or reseating is needed.
  • Never delete an object through a pointer that does not own it.

Practice exercises

  1. Does a raw pointer automatically tell you whether it owns its target?
  2. Why can unique_ptr not be copied?
  3. When is shared_ptr appropriate?
  4. Why can two objects that own each other with shared_ptr leak?

Solutions

Show exercise solutions
  1. No. A raw pointer stores an address; ownership must be established by the program's design and conventions.
  2. Copying would create two exclusive owners and make it unclear who releases the resource. Ownership transfer uses move semantics.
  3. When multiple independent components truly need to keep the same object alive for their own lifetimes.
  4. Each strong reference keeps the other's reference count above zero, so neither object reaches destruction.

Summary

Ownership is responsibility for lifetime. Prefer automatic value ownership and standard containers. Use unique_ptr for exclusive dynamic ownership and shared_ptr only when shared lifetime is truly needed. Treat raw pointers as non-owning unless a clearly documented interface says otherwise.

References