Skip to content

Commit 11c42f5

Browse files
Merge pull request #2506 from mikeblome/mb-modern-cpp
Updates to Welcome Back to C++ node
2 parents 12e6f23 + b73a461 commit 11c42f5

11 files changed

+299
-339
lines changed

.openpublishing.redirection.json

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@
5555
"redirect_url": "/cpp/overview/visual-cpp-tools-and-features-in-visual-studio-editions",
5656
"redirect_document_id": true
5757
},
58+
{
59+
"source_path": "docs/cpp/string-and-i-o-formatting-modern-cpp.md",
60+
"redirect_url": "/cpp/text/string-and-i-o-formatting-modern-cpp",
61+
"redirect_document_id": true
62+
},
5863
{
5964
"source_path": "docs/vcpkg.md",
6065
"redirect_url": "/cpp/build/vcpkg",
@@ -730,6 +735,21 @@
730735
"redirect_url": "/cpp/cpp/cpp-language-reference",
731736
"redirect_document_id": false
732737
},
738+
{
739+
"source_path": "docs/cpp/objects-own-resources-raii.md",
740+
"redirect_url": "/cpp/cpp/object-lifetime-and-resource-management-modern-cpp",
741+
"redirect_document_id": false
742+
},
743+
{
744+
"source_path": "docs/cpp/algorithms-modern-cpp.md",
745+
"redirect_url": "/cpp/cpp/welcome-back-to-cpp-modern-cpp",
746+
"redirect_document_id": false
747+
},
748+
{
749+
"source_path": "docs/cpp/containers-modern-cpp.md",
750+
"redirect_url": "/cpp/cpp/welcome-back-to-cpp-modern-cpp",
751+
"redirect_document_id": false
752+
},
733753
{
734754
"source_path": "docs/cpp/cpp-exception-handling.md",
735755
"redirect_url": "/cpp/cpp/exception-handling-in-visual-cpp",
@@ -872,17 +892,17 @@
872892
},
873893
{
874894
"source_path": "docs/c-runtime-library/reference/cos-cosf-cosl-cosh-coshf-coshl.md",
875-
"redirect_url": "/cpp/c-runtime-library/reference/cos-cosf-cosl.md",
895+
"redirect_url": "/cpp/c-runtime-library/reference/cos-cosf-cosl",
876896
"redirect_document_id": false
877897
},
878898
{
879899
"source_path": "docs/c-runtime-library/reference/sin-sinf-sinl-sinh-sinhf-sinhl.md",
880-
"redirect_url": "/cpp/c-runtime-library/reference/sin-sinf-sinl.md",
900+
"redirect_url": "/cpp/c-runtime-library/reference/sin-sinf-sinl",
881901
"redirect_document_id": false
882902
},
883903
{
884904
"source_path": "docs/c-runtime-library/reference/tan-tanf-tanl-tanh-tanhf-tanhl.md",
885-
"redirect_url": "/cpp/c-runtime-library/reference/tan-tanf-tanl.md",
905+
"redirect_url": "/cpp/c-runtime-library/reference/tan-tanf-tanl",
886906
"redirect_document_id": false
887907
},
888908
{
@@ -962,7 +982,7 @@
962982
},
963983
{
964984
"source_path": "docs/ide/cmakesettings-reference.md",
965-
"redirect_url": "/cpp/build/cmakesettings-reference.md",
985+
"redirect_url": "/cpp/build/cmakesettings-reference",
966986
"redirect_document_id": true
967987
},
968988
{
@@ -13256,4 +13276,4 @@
1325613276
"redirect_document_id": false
1325713277
}
1325813278
]
13259-
}
13279+
}

docs/cpp/algorithms-modern-cpp.md

Lines changed: 0 additions & 74 deletions
This file was deleted.

docs/cpp/auto-cpp.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ int f(int x) { return x; }
202202
int main()
203203
{
204204
auto x = f(0);
205-
const auto & y = f(1);
205+
const auto& y = f(1);
206206
int (*p)(int x);
207207
p = f;
208208
auto fp = p;

docs/cpp/containers-modern-cpp.md

Lines changed: 0 additions & 39 deletions
This file was deleted.

docs/cpp/object-lifetime-and-resource-management-modern-cpp.md

Lines changed: 56 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,19 @@
11
---
2-
title: "Object Lifetime And Resource Management (Modern C++)"
3-
ms.date: "11/04/2016"
2+
title: "Object lifetime and resource management (RAII)"
3+
description: "Follow the principle of RAII in modern C++ to avoid resource leaks."
4+
ms.date: "11/19/2019"
45
ms.topic: "conceptual"
56
ms.assetid: 8aa0e1a1-e04d-46b1-acca-1d548490700f
67
---
7-
# Object Lifetime And Resource Management (Modern C++)
8+
# Object lifetime and resource management (RAII)
89

9-
Unlike managed languages, C++ doesnt 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.
10+
Unlike managed languages, C++ doesn't have automatic *garbage collection*. That's an internal process that releases heap memory and other resources as a program runs. A C++ program is responsible for returning all acquired resources to the operating system. Failure to release an unused resource is called a *leak*. Leaked resources are unavailable to other programs until the process exits. Memory leaks in particular are a common cause of bugs in C-style programming.
1011

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.
12+
Modern C++ avoids using heap memory as much as possible by declaring objects on the stack. When a resource is too large for the stack, then it should be *owned* by an object. As the object gets initialized, it acquires the resource it owns. The object is then responsible for releasing the resource in its destructor. The owning object itself is declared on the stack. The principle that *objects own resources* is also known as "resource acquisition is initialization," or RAII.
1213

13-
## Concepts
14+
When a resource-owning stack object goes out of scope, its destructor is automatically invoked. 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.
1415

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-
void func() {
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
48-
...
49-
};
50-
node::node() : parent(...) { children.emplace_back(new node(...) ); }
51-
```
52-
53-
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 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:
16+
The following example shows a simple object `w`. It's 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`. No special code is needed in the `widget` destructor.
5817

5918
```cpp
6019
class widget {
@@ -75,7 +34,54 @@ void functionUsingWidget () {
7534
// as if "finally { w.dispose(); w.g.dispose(); }"
7635
```
7736
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.
37+
In the following example, `w` owns a memory resource and so must have code in its destructor to delete the memory.
38+
39+
```cpp
40+
class widget
41+
{
42+
private:
43+
int* data;
44+
public:
45+
widget(const int size) { data = new int[size]; } // acquire
46+
~widget() { delete[] data; } // release
47+
void do_something() {}
48+
};
49+
50+
void functionUsingWidget() {
51+
widget w(1000000); // lifetime automatically tied to enclosing scope
52+
// constructs w, including the w.data member
53+
w.do_something();
54+
55+
} // automatic destruction and deallocation for w and w.data
56+
57+
```
58+
59+
Since C++11, there's 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. Using a smart pointer eliminates the need for an explicit destructor in the `widget` class.
60+
61+
```cpp
62+
#include <memory>
63+
class widget
64+
{
65+
private:
66+
std::unique_ptr<int> data;
67+
public:
68+
widget(const int size) { data = std::make_unique<int>(size); }
69+
void do_something() {}
70+
};
71+
72+
void functionUsingWidget() {
73+
widget w(1000000); // lifetime automatically tied to enclosing scope
74+
// constructs w, including the w.data gadget member
75+
// ...
76+
w.do_something();
77+
// ...
78+
} // automatic destruction and deallocation for w and w.data
79+
80+
```
81+
82+
By using smart pointers for memory allocation, you may eliminate the potential for memory leaks. This model works for other resources, such as file handles or sockets. You can manage your own resources in a similar way in your classes. For more information, see [Smart pointers](smart-pointers-modern-cpp.md).
83+
84+
The design of C++ ensures objects are destroyed when they go out of scope. That is, they get destroyed as blocks are exited, in reverse order of construction. When an object is destroyed, its bases and members are destroyed in a particular order. Objects declared outside of any block, at global scope, can lead to problems. It may be difficult to debug, if the constructor of a global object throws an exception.
7985

8086
## See also
8187

docs/cpp/objects-own-resources-raii.md

Lines changed: 0 additions & 38 deletions
This file was deleted.

0 commit comments

Comments
 (0)