filmov
tv
C++ 03: Understand make_unique for unique_ptr
Показать описание
One of the greatest features brought by C++11 standard library is unique_ptr. It magically manages dynamically created pointers with simple ownership semantics. To create one unique_ptr for class A used in previous tutorial, you can use this statement:
So C++11 has its own method to create unique_ptr. Then C++14 standard library introduced std::make_unique() function, which is recommended for creating a unique_ptr, like this:
Which one should you choose when creating a unique_ptr? The answer is make_unique. But why?
There are four reasons that I know of. Before started, let me put two use cases in place.
Case 1: instantiate unique_ptr in function call with old and new method
Case 2: instantiate unique_ptr and assign to a variable with old and new method
If you use it in calling function in case 1:
Case 1:
It is obvious that make_unique() requires less typing.
But compare creating objects with these two methods side by side in case 2:
Case 2:
The second reason to use make_unique is no need to duplicate the type in one statement. Again, this is true when using it in calling function:
Case 1:
Class A is only used once in the new method with make_unique, but twice with old method.
But they are the same when creating and assigning to a variable as in case 2:
Case 2:
If the above two reasons are debatable, then the third reason is more solid: avoid using keyword "new".
Case 1:
Hold on. There is a fourth reason to use make_unique: it is exception safe.
First, what is exception unsafe? Consider this scenario: call function "foo" with arguments of unique_ptr and a function that may throw an exception
Function foo is called with a unique_ptr created using old method, and another function that may throw an exception. The compiler before C++17 is allowed to build the code in this order:
(1) new A(3)
(2) function_may_throw_exception()
If in the second step, the function actually throws an exception, then the newly created pointer to class A will not be deleted, since the unique_ptr wrapper has not been created. The result is a memory leak. This is known as exception unsafe.
Now if make_unique is used in calling function foo:
Note that in C++17, the exception safety issue is fixed.
It can only have two possible orders of execution:
It is described as "The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter." If you feel it's hard to understand, it just boils down to what is shown in this example.
But if you are not using C++17, using old method is still subject to exception safety concern.
Here is the list of the four reasons to use make_unique:
(1) less typing
(2) avoid repeating the type name
(3) avoid using "new" keyword
(4) exception safe
Some of these reasons are solid, some may not. You pick the reasons you like. But as a general rule of thumb, just use make_unique to keep your code consistent, and there is no real impact on performance. You use it for peace of mind. If you don't use it, you may get unpleasant surprises from nowhere.
That's enough for me to indulge in persuading unbelievers to use make_unique. Let's move on to understand a little bit more on the definition of make_unique.
I copied them here for easier reading. This is the definition for non array arguments, with template function to enable if it is not an array as highlighted in the code.
To understand the rest of the definition, let's first take a look at a standard template:
First declare the generic type used in the template: myType. Then use it to declare function arguments. In this example, the function name is Get_max() with two generic type arguments a and b. Then implement the function body: return the maximum value of the two parameters using ternary operator.
With the standard template knowledge at our disposal, let's go back to the definition of make_unique for non-array arguments. It looks a little bit different. We can see the word "template" and class type _Ty to define make_unique function. They fit the standard template pattern.
Let's review what we have learned in this tutorial:
1. Four reasons to use make_unique to create a unique_ptr
(1) less typing
(2) avoid repeating the type name
(3) avoid using "new" keyword
(4) exception safe
2. The definition of make_unique uses variadic templates that are supported since C++11
(1) dumb expansion marker: &&...
(2) ellipsis: ...
Hopefully what we have learned here gives us a good foundation to explore more features of unique_ptr. In the next tutorial, I will show you how to pass unique_ptr to a function by value or by reference.
Thanks for watching!
So C++11 has its own method to create unique_ptr. Then C++14 standard library introduced std::make_unique() function, which is recommended for creating a unique_ptr, like this:
Which one should you choose when creating a unique_ptr? The answer is make_unique. But why?
There are four reasons that I know of. Before started, let me put two use cases in place.
Case 1: instantiate unique_ptr in function call with old and new method
Case 2: instantiate unique_ptr and assign to a variable with old and new method
If you use it in calling function in case 1:
Case 1:
It is obvious that make_unique() requires less typing.
But compare creating objects with these two methods side by side in case 2:
Case 2:
The second reason to use make_unique is no need to duplicate the type in one statement. Again, this is true when using it in calling function:
Case 1:
Class A is only used once in the new method with make_unique, but twice with old method.
But they are the same when creating and assigning to a variable as in case 2:
Case 2:
If the above two reasons are debatable, then the third reason is more solid: avoid using keyword "new".
Case 1:
Hold on. There is a fourth reason to use make_unique: it is exception safe.
First, what is exception unsafe? Consider this scenario: call function "foo" with arguments of unique_ptr and a function that may throw an exception
Function foo is called with a unique_ptr created using old method, and another function that may throw an exception. The compiler before C++17 is allowed to build the code in this order:
(1) new A(3)
(2) function_may_throw_exception()
If in the second step, the function actually throws an exception, then the newly created pointer to class A will not be deleted, since the unique_ptr wrapper has not been created. The result is a memory leak. This is known as exception unsafe.
Now if make_unique is used in calling function foo:
Note that in C++17, the exception safety issue is fixed.
It can only have two possible orders of execution:
It is described as "The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter." If you feel it's hard to understand, it just boils down to what is shown in this example.
But if you are not using C++17, using old method is still subject to exception safety concern.
Here is the list of the four reasons to use make_unique:
(1) less typing
(2) avoid repeating the type name
(3) avoid using "new" keyword
(4) exception safe
Some of these reasons are solid, some may not. You pick the reasons you like. But as a general rule of thumb, just use make_unique to keep your code consistent, and there is no real impact on performance. You use it for peace of mind. If you don't use it, you may get unpleasant surprises from nowhere.
That's enough for me to indulge in persuading unbelievers to use make_unique. Let's move on to understand a little bit more on the definition of make_unique.
I copied them here for easier reading. This is the definition for non array arguments, with template function to enable if it is not an array as highlighted in the code.
To understand the rest of the definition, let's first take a look at a standard template:
First declare the generic type used in the template: myType. Then use it to declare function arguments. In this example, the function name is Get_max() with two generic type arguments a and b. Then implement the function body: return the maximum value of the two parameters using ternary operator.
With the standard template knowledge at our disposal, let's go back to the definition of make_unique for non-array arguments. It looks a little bit different. We can see the word "template" and class type _Ty to define make_unique function. They fit the standard template pattern.
Let's review what we have learned in this tutorial:
1. Four reasons to use make_unique to create a unique_ptr
(1) less typing
(2) avoid repeating the type name
(3) avoid using "new" keyword
(4) exception safe
2. The definition of make_unique uses variadic templates that are supported since C++11
(1) dumb expansion marker: &&...
(2) ellipsis: ...
Hopefully what we have learned here gives us a good foundation to explore more features of unique_ptr. In the next tutorial, I will show you how to pass unique_ptr to a function by value or by reference.
Thanks for watching!
Комментарии