Resource Management and Scope
Objects on the free store are not released automatically at scope exit, so the standard library provides smart pointers to help manage them.
Why Scope Alone Is Not Enough
When a function ends, the local variables declared inside it go away, and the resources they hold are released with them. Programmers rarely have to think about this, because it happens automatically at the edge of the scope. Objects created on the free store are different. They are not tied to a scope in the same way, so the question of who releases them, and when, needs an explicit answer.
What do you think happens?
A function creates an object with new, stores its address in a local pointer, and then returns. Does the object the pointer pointed to get released when the pointer goes out of scope?
Reveal answer
No, the object lives on the free store and is not released automatically at scope exit.
The pointer is a scope-bound variable and disappears at scope exit, but the object it pointed to is not tied to that scope. Nothing releases it automatically, so the program must arrange for that release. That gap is the reason smart pointers exist.
Scope-Bound Objects and Free Store Objects
Objects defined in a scope already have their resources released when the program exits that scope. That handles a large group of objects automatically. The harder question is what happens to objects allocated on the free store, because they are not tied to a scope in the same way. The diagram below separates the two situations: the pointer is a local name that lives inside the scope, while the object it refers to sits on the free store and outlives that scope unless something releases it.
Read the diagram as two lifetimes. The pointer's lifetime ends with the scope, while the object's lifetime is not set by the scope at all. If nothing else releases the object, it stays allocated after the pointer is gone, and the program has lost the only name it had for it. This is the situation that smart pointers are designed to prevent.
How Memory Leaks Happen
A memory leak occurs when memory that a program obtained is no longer released, typically because the program lost track of it. The source attributes leaks to careless programming. The cause is rarely a single bad line. It is usually a path through the code that reaches the end of a function, or leaves it early, without ever reaching the release.
- The new expression obtains memory from the free store.
- The early return skips every line after it, including the delete[].
- When fail is true, the memory is no longer released, and the pointer that tracked it is gone.
The flowchart traces which paths reach the delete call. Only one of the two outcomes releases the memory. The early return is the path that leaks, even though the programmer wrote a correct delete for the normal case.
Smart Pointers as Owners
The standard library provides two smart pointers, declared in the memory header, for managing objects on the free store. The names describe the ownership model. A unique_ptr is meant to be the only owner of the object it manages. A shared_ptr is meant to be one of several owners sharing the same object. The most basic use of either one is to prevent memory leaks caused by careless programming, and choosing between them depends on whether one owner or several will manage the object.
Destructors Complete the Constructor's Duty
A class often needs something from outside itself to do its job: memory, a file, a lock, or some other limited resource. When a constructor obtains such a resource, it takes on a responsibility. The resource has to be given back at some point, and the class is the natural place for that to happen. The source's guideline is direct: if a constructor acquires a resource, the class needs a destructor to release it. Without that destructor, nothing in the class's own code returns the resource, and it stays held after the object that needed it is gone.
- The constructor marks the start: the object takes control of the array.
- The destructor marks the end: the object gives the array back.
- Between those two points the object can use the array freely.
Notice what the code does not contain: any step where the programmer must remember to release the array by hand. The release is attached to the object's end, not to a line that a particular path might skip. Any code that creates a Buffer and lets it go out of scope gets the release automatically.
RAII and Resource Handles
The source recommends resource handles and RAII to manage resources. RAII stands for Resource Acquisition Is Initialization. The name describes the pattern: a resource is acquired during initialization of an object, and because of that, the object's lifetime controls the resource. The object's destructor performs the release. A resource handle is an object whose job is to hold a resource on behalf of the code that uses it. The handle acquires the resource when it is created and releases it when it is destroyed, so the programmer's attention can stay on the work rather than on bookkeeping.
Read the sequence from top to bottom. Acquisition happens when the handle is created, before any use. Use happens in the middle, and data flows in both directions: requests and values travel from the calling code into the handle, which forwards them to the resource, and results travel back the same way. The release comes last, and it happens even when the calling code leaves early. The calling code never needs to know when or how the resource is returned, because the handle owns that decision.
Tracing a handle through an early exit
A function creates a Buffer handle, then returns early before the function's normal end. When does the array get released?
- 1Create the handle
The Buffer constructor runs and acquires the array. The resource now belongs to the handle.
- 2Leave early
The early return ends the function's scope before the final line runs.
- 3Destroy the handle
The Buffer destructor runs as the object's scope ends, and it releases the array.
The array is released on the early path too, because the release is tied to the object's lifetime rather than to a line the function reaches.
Naked new and delete Versus Managed Alternatives
The source advises avoiding naked new and delete operations. A naked new or delete is one that appears directly in ordinary code, with nothing managing the resource it produces. Every naked new must be matched by a delete somewhere else, and the programmer must make sure that match happens on every path through the code. Naked new and delete put the burden of matching every release on the programmer, which is easy to underestimate. A managed alternative, such as a smart pointer or a resource handle, takes that burden over.
| Aspect | Naked new and delete | Managed alternative |
|---|---|---|
| Who matches each release? | The programmer, on every path | The owning object, handled automatically |
| Early return or exception | Can skip the delete and leak | The owner's release still happens |
| Number of owners | Not stated in the code | unique_ptr for one owner, shared_ptr for several |
| Main risk | Forgotten or mismatched delete | Choosing the wrong ownership model |
Manual cleanup compared with a managed alternative
Initializer-List Constructors for Containers
The last guideline in this section concerns container classes. A container is a class that holds a collection of elements. The source says that if a class is a container, it should be given an initializer-list constructor. This constructor accepts a list of values written in braces, so a user can create the container already filled with those values. This makes the container easier to create and read, and it matches the way programmers commonly write small collections of values.
- The braced list 1, 2, 3 is passed to the constructor as the initializer list.
- The container is populated at the moment it is created.
- This is a general sketch; the source does not describe further details of this constructor.
Common Mistakes with Resource Management
Common mistake Assuming an object allocated with new is released when its pointer goes out of scope
What it looks likeA function stores the result of new in a local pointer and returns without any delete.
Why it failsObjects on the free store are not released automatically at scope exit. Only the pointer disappears.
Do this insteadGive the object an owner, such as a unique_ptr, or make sure a matching delete runs on every path.
Common mistake Forgetting the delete on an early return
What it looks likeThe function calls new, returns early on an error, and reaches delete only on the normal path.
Why it failsThe early return skips the delete, so the memory is leaked.
Do this insteadUse a smart pointer or a resource handle so the release does not depend on which path runs.
Common mistake Writing a constructor that acquires a resource with no destructor
What it looks likeA class allocates an array in its constructor and never defines a destructor.
Why it failsNothing in the class's own code returns the resource, so it stays held after the object is gone.
Do this insteadAdd a destructor that releases the resource the constructor acquired.
Common mistake Using shared_ptr when there is only one owner
What it looks likeA single function creates a shared_ptr and never shares it with anything.
Why it failsshared_ptrrepresents shared ownership. When one owner is clear, the ownership model does not match the design.Do this insteadUse unique_ptr when a single owner manages the object.
Common mistake Writing naked new and delete in ordinary code instead of relying on a managed alternative
Practice Tracing Resource Lifetimes
A function allocates an object with new, performs a check, and returns early when the check fails. The delete is placed only at the end of the function. Identify the path that leaks memory, then describe two different ways to fix it using ideas from this article.
Hints
- Trace each path from the allocation to the end of the function, including the early return.
- Ask which path reaches the delete, and which path leaves without reaching it.
- A smart pointer or a resource handle ties the release to an owner rather than to a particular line.
Key Takeaways
- Objects on the free store are not released automatically at scope exit, so the program or a managing object must release them.
- Memory leaks come from careless paths through code, such as early returns, and smart pointers help prevent them by giving each object a clear owner.
unique_ptrrepresents unique ownership with one owner, whileshared_ptrrepresents shared ownership among several owners; choose based on how many owners will manage the object.- A constructor that acquires a resource needs a destructor that releases it, and RAII ties the resource's lifetime to the object's lifetime through resource handles.
- Naked new and delete put the burden of matching every release on the programmer, so a managed alternative is preferred, and a container class should provide an initializer-list constructor.