make_unique and make_shared are more concise than explicitly calling the constructor of unique_ptr and
shared_ptr since they don’t require specifying the type multiple times and eliminate the need to use new.
make_unique and make_shared should also be preferred for exception-safety and performance reasons.
Exception-Safety
While make_unique and make_shared are exception-safe, complex constructions of unique_ptr and
shared_ptr might not be because C++ allows arbitrary order of evaluation of subexpressions (until C++17).
Consider this example:
f(unique_ptr<Lhs>(new Lhs()), throwingFunction());
The following scenario can happen:
- Memory allocation for
Lhs
- Construction of the
Lhs object
- Call to
throwingFunction (before the unique_ptr construction)
-
throwingFunction throws an exception
- The constructed
Lhs object is leaked since the unique_ptr isn’t constructed yet
Note: This scenario can only happen before C++17. Since C++17, the standard states that even though the order of evaluation of each argument is
still unspecified, interleaving the evaluation of different arguments is no longer allowed. This makes the direct construction of
unique_ptr and shared_ptr exception-safe.
Performance
Using make_unique() doesn’t impact performance, but make_shared() improves it slightly.
Indeed, constructing
explicitly a shared_ptr() requires two heap allocations: one for the managed object and the other for the control block that stores data
about the ref-counts and the shared_ptr() deleter. make_shared() on the other hand, performs only one heap allocation.
Note: Because make_shared performs only one allocation for both the object and the control block, the memory occupied by the object
will be deallocated when no shared_ptr or weak_ptr points to it. If the object is large, a weak_ptr is used,
and memory is a concern, explicitly calling the constructor of shared_ptr may be preferred. This way, the object’s memory will be
deallocated when there are no more shared owners, independently of any weak_ptrs.
Noncompliant code example
std::unique_ptr<MyClass> uniqueP(new MyClass(42)); // Noncompliant
std::shared_ptr<MyClass> sharedP(new MyClass(42)); // Noncompliant
Compliant solution
auto uniqueP = std::make_unique<MyClass>(42);
auto sharedP = std::make_shared<MyClass>(42);
Exceptions
This rule ignores code that uses features not supported by make_shared and make_unique:
std::unique_ptr<std::FILE, std::function<void(std::FILE*)>> file(
fopen("example.txt", "r"),
[](FILE* inFile) { fclose(inFile); }); // Compliant: custom deleter is specified
- calling placement-new, i.e., version of
new with arguments, like new(std::nothrow)
In addition, make_shared does not support the following:
- custom operator
new
- allocating arrays (before C++20)