Advantages of using std::make_unique over new operator
Advantages
make_unique
teaches users "never saynew
/delete
andnew[]
/delete[]
" without disclaimers.make_unique
shares two advantages withmake_shared
(excluding the third advantage, increased efficiency). First,unique_ptr<LongTypeName> up(new LongTypeName(args))
must mentionLongTypeName
twice, whileauto up = make_unique<LongTypeName>(args)
mentions it once.make_unique
prevents the unspecified-evaluation-order
leak triggered by expressions likefoo(unique_ptr<X>(new X), unique_ptr<Y>(new Y))
. (Following the advice "never saynew
" is simpler than
"never saynew
, unless you immediately give it to a namedunique_ptr
".)make_unique
is carefully implemented for exception safety and is recommended over directly callingunique_ptr
constructors.
When not to use make_unique
- Don't use
make_unique
if you need a custom deleter or are adopting a raw pointer from elsewhere.
Sources
- Proposal of
std::make_unique
. - Herb Sutter's GotW #89 Solution: Smart Pointers
Why use std::make_unique in C++17?
You're right that the main reason was removed. There are still the don't use new guidelines and that it is less typing reasons (don't have to repeat the type or use the word new
). Admittedly those aren't strong arguments but I really like not seeing new
in my code.
Also don't forget about consistency. You absolutely should be using make_shared
so using make_unique
is natural and fits the pattern. It's then trivial to change std::make_unique<MyClass>(param)
to std::make_shared<MyClass>(param)
(or the reverse) where the syntax A requires much more of a rewrite.
Differences between std::make_unique and std::unique_ptr with new
The motivation behind make_unique
is primarily two-fold:
make_unique
is safe for creating temporaries, whereas with explicit use ofnew
you have to remember the rule about not using unnamed temporaries.foo(make_unique<T>(), make_unique<U>()); // exception safe
foo(unique_ptr<T>(new T()), unique_ptr<U>(new U())); // unsafe*The addition of
make_unique
finally means we can tell people to 'never' usenew
rather than the previous rule to "'never' usenew
except when you make aunique_ptr
".
There's also a third reason:
make_unique
does not require redundant type usage.unique_ptr<T>(new T())
->make_unique<T>()
None of the reasons involve improving runtime efficiency the way using make_shared
does (due to avoiding a second allocation, at the cost of potentially higher peak memory usage).
* It is expected that C++17 will include a rule change that means that this is no longer unsafe. See C++ committee papers P0400R0 and P0145R3.
std::make_unique with placement new
There is no standard way to do this, because standard C++ doesn't provide any real support for VLAs/flexible array members. make_unique
was written solely for either:
- A single object of fixed size (it has no mechanism to provide information about the "real" size, it just uses
new
, which assumessizeof T
is correct and complete) - An array of objects of fixed size (by definition an array must have objects of fixed size, or indexing doesn't work)
I suppose in theory you could make your own version that supported quasi-flexible array members, but there is zero support for this in the C++ standard library. std::make_unique
doesn't even have an allocator-aware variant; it's not going out of its way to support unusual use cases. std::allocate_shared
(the allocator aware version of std::make_shared
) might be forced to support this in ridiculous ways (you'd essentially need to write custom allocators that also knew how much extra memory an allocation request should provide), and there's a proposal for an allocator-aware version of std::make_unique
, but again, making this work would be an exercise in insanity.
You say "as good practice, I'd prefer to use std::make_unique
, over a raw new
", but flexible array members in C++ are already bad practice; there is essentially zero support for them because they break all sorts of assumptions that much of the C++ language and library rely on. If you must do it, write your own factory methods to perform the work; sure, using raw new
is frowned on, but confining it to a single API in your code that guarantees the result is a managed pointer before passing it outside that API limits the "damage".
Using std::make_unique with the GetProfileBinary function call
pDateTime
is supposed to be nullptr
, and GetProfileBinary
handles the allocation. Code Analysis mistakenly thinks you forgot the allocation.
It does need to check for success before calling delete[]
. We can't use delete[]pDatTime
because pDatTime
is not an array. But GetProfileBinary
allocates using new BYTE[size]
, so we need to cast back to BYTE
.
You can also add a NULL
check before reading pDatTime
, that might make Code Analysis happy.
if (pDatTime && uSize == sizeof(DATE))
rBackupDate = *pDatTime;
else
rBackupDate = COleDateTime::GetCurrentTime();
if(pDatTime) delete[](BYTE*)pDatTime;
You can use std::unique_ptr<BYTE[]> cleanup((BYTE*)pDatTime)
for deletion, but this has to be after GetProfileBinary
is called.
Example:
DATE* pDatTime = nullptr;
GetProfileBinary(_T("Options"), _T("BackupLastBackupDate"), (LPBYTE*)(&pDatTime), &uSize);
std::unique_ptr<BYTE[]> cleanup((BYTE*)pDatTime); //automatic delete
if (pDatTime && uSize == sizeof(DATE))
rBackupDate = *pDatTime;
else
rBackupDate = COleDateTime::GetCurrentTime();
//pDatTime = NULL; <- Error when used with unique_ptr
...
//pDatTime is deleted later, when `cleanup` goes out of scope
Assignment of unique_ptr inside member initializer list
The only difference is style. Since use of operator new
is now considered old fashioned std::make_unique
is recommended.
For compiler there is no difference.
By the way is should be:
A::A() : m_b{std::make_unique<B>()} {}
Also see
CppCoreGuidelines/CppCoreGuidelines.md at master · isocpp/CppCoreGuidelines
C.150: Use
make_unique()
to construct objects owned byunique_ptr
s
Reason
Example
make_unique
gives a more concise statement of the construction. It also ensures exception safety in complex expressions.unique_ptr<Foo> p {new Foo{7}}; // OK: but repetitive
auto q = make_unique<Foo>(7); // Better: no repetition of Foo
// Not exception-safe: the compiler may interleave the computations of arguments as follows:
//
// 1. allocate memory for Foo,
// 2. construct Foo,
// 3. call bar,
// 4. construct unique_ptr<Foo>.
//
// If bar throws, Foo will not be destroyed, and the memory-allocated for it will leak.
f(unique_ptr<Foo>(new Foo()), bar());
// Exception-safe: calls to functions are never interleaved.
f(make_unique<Foo>(), bar());
Is there a way to let make_unique T[ ] can forward arguments to constructor of T?
I think the idea propsed by @AchimGuetlein is better.
Define as following:
vector<unique_ptr<ClassA> > container;
for (i = 0; i<number of queues; ++i)
container.emplace_back(make_unique<ClassA>(args to constructor of ClassA));
instead of :
unique_ptr<ClassA [] > ques = make_unique<ClassA [] >(n, ...);
Although elements of vector might be moved, there is no moving occured to my queues. The objs moved just are unique_ptr self, but the addresses of the queues would never be changed.
Sorry for committing it as an answer, since the code sample can not be gracefully showed as codelet in a comment.
Related Topics
Why Do I See Strange Values When I Print Uninitialized Variables
C++ "Cin" Only Reads the First Word
C++11 - Static_Assert Within Constexpr Function
Throwing Exceptions from Constructors
Why Would One Use Nested Classes in C++
What New Capabilities Do User-Defined Literals Add to C++
Why Does Printf() Promote a Float to a Double
How to Concatenate Multiple C++ Strings on One Line
Static Variables in Member Functions
What Are Some Reasons a Release Build Would Run Differently Than a Debug Build
Why Do Std::Shared_Ptr≪Void≫ Work
Can Placement New For Arrays Be Used in a Portable Way
Template Specialization of Particular Members
What Kind of Optimization Does Const Offer in C/C++
Difference Between Undefined Behavior and Ill-Formed, No Diagnostic Message Required
Is It a Good Practice to Place C++ Definitions in Header Files