You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# Object Lifetime And Resource Management (Modern C++)
8
8
9
-
Unlike managed languages, C++ doesn’t have garbage collection (GC), which automatically releases no-longer-used memory resources as a program runs. In C++, resource management is directly related to object lifetime. This document describes the factors that affect object lifetime in C++ and how to manage it.
9
+
Unlike managed languages, C++ has no garbage collection (GC) process that releases heap memory and other resources as a program runs. In modern C++, all resources and memory that a program acquires should have an object that owns them. An owning object is responsible for releasing its resources in its destructor. When an object goes out of scope, the destructor is automatically invoked. If the object owns a resource, then its destructor should return the resource, for example by calling [delete](). In this way, garbage collection in C++ is closely related to object lifetime and is deterministic. A resource is always released at a known point in the program, which you can control. Only deterministic destructors like those in C++ can handle memory and non-memory resources equally. C++ is designed to ensure that objects are destroyed at the correct times, that is, as blocks are exited, in reverse order of construction. When an object is destroyed, its bases and members are destroyed in a particular order.
10
10
11
-
C++ doesn’t have GC primarily because it doesn't handle non-memory resources. Only deterministic destructors like those in C++ can handle memory and non-memory resources equally. GC also has other problems, like higher overhead in memory and CPU consumption, and locality. But universality is a fundamental problem that can't be mitigated through clever optimizations.
11
+
Use static lifetime sparingly (global static, function local static) because problems can arise. When the constructor of a global object throws an exception, typically, the app faults in a way that can be difficult to debug. Construction order is problematic for static lifetime objects, and is not concurrency-safe. In addition, destruction order can be complex, especially where polymorphism is involved. Even if your object or variable isn’t polymorphic and doesn't have complex construction/destruction ordering, there’s still the issue of thread-safe concurrency. A multithreaded app can’t safely modify the data in static objects without having thread-local storage, resource locks, and other special precautions.
12
12
13
-
## Concepts
14
-
15
-
An important thing in object-lifetime management is the encapsulation—whoever's using an object doesn't have to know what resources that object owns, or how to get rid of them, or even whether it owns any resources at all. It just has to destroy the object. The C++ core language is designed to ensure that objects are destroyed at the correct times, that is, as blocks are exited, in reverse order of construction. When an object is destroyed, its bases and members are destroyed in a particular order. The language automatically destroys objects, unless you do special things like heap allocation or placement new. For example, [smart pointers](../cpp/smart-pointers-modern-cpp.md) like `unique_ptr` and `shared_ptr`, and C++ Standard Library containers like `vector`, encapsulate **new**/**delete** and `new[]`/`delete[]` in objects, which have destructors. That's why it's so important to use smart pointers and C++ Standard Library containers.
16
-
17
-
Another important concept in lifetime management: destructors. Destructors encapsulate resource release. (The commonly used mnemonic is RRID, Resource Release Is Destruction.) A resource is something that you get from "the system" and have to give back later. Memory is the most common resource, but there are also files, sockets, textures, and other non-memory resources. "Owning" a resource means you can use it when you need it but you also have to release it when you're finished with it. When an object is destroyed, its destructor releases the resources that it owned.
18
-
19
-
The final concept is the DAG (Directed Acyclic Graph). The structure of ownership in a program forms a DAG. No object can own itself—that's not only impossible but also inherently meaningless. But two objects can share ownership of a third object. Several kinds of links are possible in a DAG like this: A is a member of B (B owns A), C stores a `vector<D>` (C owns each D element), E stores a `shared_ptr<F>` (E shares ownership of F, possibly with other objects), and so forth. As long as there are no cycles and every link in the DAG is represented by an object that has a destructor (instead of a raw pointer, handle, or other mechanism), then resource leaks are impossible because the language prevents them. Resources are released immediately after they're no longer needed, without a garbage collector running. The lifetime tracking is overhead-free for stack scope, bases, members, and related cases, and inexpensive for `shared_ptr`.
20
-
21
-
### Heap-based lifetime
22
-
23
-
For heap object lifetime, use [smart pointers](../cpp/smart-pointers-modern-cpp.md). Use `shared_ptr` and `make_shared` as the default pointer and allocator. Use `weak_ptr` to break cycles, do caching, and observe objects without affecting or assuming anything about their lifetimes.
24
-
25
-
```cpp
26
-
voidfunc() {
27
-
28
-
auto p = make_shared<widget>(); // no leak, and exception safe
29
-
...
30
-
p->draw();
31
-
32
-
} // no delete required, out-of-scope triggers smart pointer destructor
33
-
```
34
-
35
-
Use `unique_ptr` for unique ownership, for example, in the *pimpl* idiom. (See [Pimpl For Compile-Time Encapsulation](../cpp/pimpl-for-compile-time-encapsulation-modern-cpp.md).) Make a `unique_ptr` the primary target of all explicit **new** expressions.
36
-
37
-
```cpp
38
-
unique_ptr<widget> p(new widget());
39
-
```
40
-
41
-
You can use raw pointers for non-ownership and observation. A non-owning pointer may dangle, but it can’t leak.
42
-
43
-
```cpp
44
-
class node {
45
-
...
46
-
vector<unique_ptr<node>> children; // node owns children
47
-
node* parent; // node observes parent, which is not a concern
When performance optimization is required, you might have to use *well-encapsulated* owning pointers and explicit calls to delete. An example is when you implement your own low-level data structure.
54
-
55
-
### Stack-based lifetime
56
-
57
-
In modern C++, *stack-based scope* is a powerful way to write robust code because it combines automatic *stack lifetime* and *data member lifetime* with high efficiency—lifetime tracking that is essentially free of overhead. Heap object lifetime requires diligent manual management and can be the source of resource leaks and inefficiencies, especially when you are working with raw pointers. Consider this code, which demonstrates stack-based scope:
13
+
The principle that *objects own resources* is also known as "resource acquisition is initialization" or "RAII". The following example shows a simple object `w`. It is declared on the stack at function scope, and is destroyed at the end of the function block. The object `w` owns no *resources* (such as heap-allocated memory). Its only member `g` is itself declared on the stack and simply goes out of scope along with `w`. Therefore, no special code is needed in the `widget` destructor.
58
14
59
15
```cpp
60
16
classwidget {
@@ -75,7 +31,54 @@ void functionUsingWidget () {
75
31
// as if "finally { w.dispose(); w.g.dispose(); }"
76
32
```
77
33
78
-
Use static lifetime sparingly (global static, function local static) because problems can arise. What happens when the constructor of a global object throws an exception? Typically, the app faults in a way that can be difficult to debug. Construction order is problematic for static lifetime objects, and is not concurrency-safe. Not only is object construction a problem, destruction order can be complex, especially where polymorphism is involved. Even if your object or variable isn’t polymorphic and doesn't have complex construction/destruction ordering, there’s still the issue of thread-safe concurrency. A multithreaded app can’t safely modify the data in static objects without having thread-local storage, resource locks, and other special precautions.
34
+
In the following example, `w` owns a memory resource and therefore must have code in its destructor to delete the memory.
35
+
36
+
```cpp
37
+
class widget
38
+
{
39
+
private:
40
+
int* data;
41
+
public:
42
+
widget(const int size) { data = new int[size]; } // acquire
43
+
~widget() { delete[] data; } // release
44
+
void do_something() {}
45
+
};
46
+
47
+
void functionUsingWidget() {
48
+
widget w(1000000); // lifetime automatically tied to enclosing scope
49
+
// constructs w, including the w.data member
50
+
w.do_something();
51
+
52
+
} // automatic destruction and deallocation for w and w.data
53
+
54
+
```
55
+
56
+
Since C++11, there is a better way to write the previous example, by using a smart pointer from the Standard Library. The smart pointer handles the allocation and deletion of the memory it owns. This eliminates the need for an explicit destructor in the `widget` class.
57
+
58
+
```cpp
59
+
#include<memory>
60
+
classwidget
61
+
{
62
+
private:
63
+
std::unique_ptr<int> data;
64
+
public:
65
+
widget(const int size) { data = std::make_unique<int>(size); }
66
+
void do_something() {}
67
+
};
68
+
69
+
voidfunctionUsingWidget() {
70
+
widget w(1000000); // lifetime automatically tied to enclosing scope
71
+
// constructs w, including the w.data gadget member
72
+
// ...
73
+
w.do_something();
74
+
// ...
75
+
} // automatic destruction and deallocation for w and w.data
76
+
77
+
```
78
+
79
+
By using smart pointers for memory allocation, and handling other resources such as file handles, sockets, and so on in a similar way in your own classes, you can eliminate the potential for memory leaks. For more information, see
0 commit comments