rspec/rules/S5950/cfamily/rule.adoc
Fred Tingaud d3cfe19d7e
Fix broken or dangerous backquotes
Co-authored-by: Marco Borgeaud <89914223+marco-antognini-sonarsource@users.noreply.github.com>
2023-10-30 10:33:56 +01:00

89 lines
3.6 KiB
Plaintext

== Why is this an issue?
`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 {cpp} allows arbitrary order of evaluation of subexpressions (until {cpp}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 {cpp}17. Since {cpp}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_ptr``s.
=== Noncompliant code example
[source,cpp]
----
std::unique_ptr<MyClass> uniqueP(new MyClass(42)); // Noncompliant
std::shared_ptr<MyClass> sharedP(new MyClass(42)); // Noncompliant
----
=== Compliant solution
[source,cpp]
----
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`:
* custom deleters
[source,cpp]
----
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 {cpp}20)
== Resources
* {cpp] Core Guidelines - https://github.com/isocpp/CppCoreGuidelines/blob/036324/CppCoreGuidelines.md#c150-use-make_unique-to-construct-objects-owned-by-unique_ptrs[C.150 - Use make_unique() to construct objects owned by unique_ptrs]
* {cpp] Core Guidelines - https://github.com/isocpp/CppCoreGuidelines/blob/036324/CppCoreGuidelines.md#c151-use-make_shared-to-construct-objects-owned-by-shared_ptrs[C.151 - Use make_shared() to construct objects owned by shared_ptrs]
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
=== Message
Use "(make_unique/make_shared)" instead.
endif::env-github,rspecator-view[]