Concepts / Dynamic Memory on the Free Store

Dynamic Memory on the Free Store

Objects on the free store are not released automatically at scope exit, so the standard library provides smart pointers to help manage them.

  • Programming

Why Leaving a Scope Does Not Clean Up Everything

Most of the objects you write in a program are defined inside a scope, such as a function body or a loop body. When the program leaves that scope, the resources of those objects are released for you. This happens without any extra work from the programmer, and it covers a large group of objects.

What do you think happens?

If an object is created on the free store inside a function, and the function returns, is the object released automatically at that point?

  • Yes, it is released when the function returns, just like a local variable

  • No, it is not tied to the scope in the same way and is not released automatically at scope exit

Reveal answer
Answer:

No, it is not released automatically at scope exit

Objects defined in a scope have their resources released when the program leaves that scope. Objects allocated on the free store are not tied to a scope in the same way, so their release has to be handled another way. That is the problem smart pointers were built to address.

Stack Lifetime Versus Free Store Lifetime

The lifetime of an object describes how long it stays in existence. An object defined in a scope lives for as long as that scope is active. When execution reaches the end of the scope, the object's resources are released. Programmers rarely need to think about this, because it happens automatically.

An object allocated on the free store is different. Its lifetime is not connected to the scope in which it was created. The object continues to exist after the enclosing block ends, which is useful when the object must outlive the code that created it. The cost of this flexibility is that nothing automatically releases the object when the block ends. The program has to make sure the object is eventually released, and that responsibility is exactly where mistakes happen.

Key point

Scope-based cleanup covers objects defined in a block. Free store objects are not covered by scope-based cleanup, so something else must be responsible for releasing them.

How a Memory Leak Happens

A memory leak occurs when memory that a program obtained is no longer released, typically because the program lost track of it. Once the program has lost track of the memory, it can no longer release it, even though the memory is still held. The source attributes leaks to careless programming, which means the problem is usually not a deep technical failure but a missed step in the code.

The flowchart below shows why a manual release is fragile. Every path through the function must reach the release step. If one path leaves the function early, the release step is skipped, and the allocated object becomes unreachable without ever being freed.

allocate with newuse the objectearly return orerror?return withoutreleaseobject is lost and leakedcall deletememory releasedyesno
Which execution paths reach the manual delete, and which skip it so that the allocated object is never freed?
cpp
Notes on the code
  • The object is allocated on the free store with new
  • If fail is true, the function returns before the delete line runs
  • On that path the object is no longer reachable and is never released

Smart Pointers as Guardians of Free Store Objects

Definition

A smart pointer is an object that manages an object allocated on the free store. The standard library provides smart pointers in the <memory> header. Their most basic purpose is to stop memory leaks caused by careless programming, because the smart pointer takes responsibility for the managed object so the programmer does not have to remember every release step.

cpp
Notes on the code
  • The smart pointer w is declared in the function scope and owns the Widget
  • The early return is still safe, because the smart pointer does not depend on a delete line that might be skipped
  • The same code path that leaked in the previous example no longer leaks

Two Ownership Models

The two smart pointers in <memory> are named after the ownership model they represent. A unique_ptr represents unique ownership, and a shared_ptr represents shared ownership. Choosing between them depends on whether one owner or several owners will manage the object. The comparison below summarizes the difference before the details are explained.

unique_ptrone ownermeant to be the only ownerobject released withits single ownershared_ptrseveral ownersone of several sharing theobjectobject released afterthe last owner
How does one owner differ from many co-owners, and what happens to the managed object in each case?

Tracing Shared Ownership Step by Step

The diagram below follows one shared object through a sequence of events. Start with a single owner. Each time another shared_ptr is made to refer to the object, the number of owners rises. Each time one of those owners goes out of scope, the number falls. The object is freed when the last owner lets go.

1 ownerfirst shared_ptr created2 ownersa copy is made3 ownersanother copy is made2 ownersone copy goes out of scope1 owneranother copy goes out ofscopeobject freedlast owner gonecopy madecopy madescope endsscope endsscope ends
How does the number of owners rise and fall as shared_ptr copies are made and go out of scope, and when is the object finally freed?
Worked example

Following a Shared Object Through Three Owners

Three parts of a program each hold a shared_ptr to the same object. Determine when the object is freed as each part finishes with it.

  1. 1
    Step 1

    The first part holds the only owner. The object is alive because an owner exists.

  2. 2
    Step 2

    The second and third parts each receive a copy. The object now has three co-owners, all sharing the same object.

  3. 3
    Step 3

    The first and second parts finish and go out of scope. One owner remains, so the object is still alive and still in use by the third part.

  4. 4
    Step 4

    The third part finishes and goes out of scope. No owners remain, so the object is freed.

Result

The object is freed only after the last shared_ptr that refers to it goes away, not when the first owner finishes.

Mistakes Beginners Make With Ownership

  • Common mistake

    Assuming a free store object is released when its enclosing scope ends

    What it looks like

    Creating an object with new inside a function and expecting it to disappear when the function returns

    Why it fails

    Objects allocated on the free store are not tied to a scope in the same way as objects defined in a block

    Do this instead

    Give the object a smart pointer owner, or make sure a release step runs on every path

  • Common mistake

    Relying on a manual release step that can be skipped

    What it looks like

    Placing a delete at the end of a function that has an early return before it

    Why it fails

    Careless programming is the source of leaks, and an early return can bypass the release step

    Do this instead

    Let a smart pointer own the object so its release does not depend on reaching one specific line

  • Common mistake

    Using shared_ptr when only one owner is needed

    What it looks like

    Making every smart pointer a shared_ptr by default

    Why it fails

    A shared_ptr is meant to be one of several owners, so it describes a shared model the design does not need

    Do this instead

    Use unique_ptr when one owner is responsible, and reserve shared_ptr for several co-owners

Checking Your Understanding

MEDIUM

A function creates an object with new and calls delete at the end of the function. Explain in your own words how an early return could cause a memory leak, and rewrite the idea using a smart pointer. Then decide whether unique_ptr or shared_ptr fits a design where one part of the program alone decides when the object is no longer needed.

Hints
  • Trace the path through the function that returns before the delete line
  • Think about whether the object is reachable after the function returns early
  • A unique_ptr expresses exactly one owner; a shared_ptr expresses several owners

Key Takeaways

  • Objects defined in a scope are released when the program leaves that scope, but objects allocated on the free store are not tied to a scope in the same way and need another mechanism for release
  • A memory leak occurs when memory is no longer released, typically because the program lost track of it, and smart pointers exist primarily to prevent leaks caused by careless programming
  • The standard library's smart pointers in the memory header are unique_ptr for unique ownership, where one owner manages the object, and shared_ptr for shared ownership, where several owners share the object
  • Choosing between unique_ptr and shared_ptr depends on whether one owner or several owners will manage the object

Related concepts

More concepts starting with D