Rule A18-1-4 (required, implementation, automated)
A pointer pointing to an element of an array of objects shall not be passed to a smart pointer of single object type.
Rationale
A dynamically allocated array of objects needs a corresponding deallocation function,
e.g. allocation by new[] requires deallocation by delete[], see also rule A18-5-3 in
section 6.18.5. Smart pointers of a single object type, e.g. std::unique_ptr
smart pointer. With the standard library smart pointer templates std::unique_ptr and
std::shared_ptr, this is possible when calling the constructor or the reset function.
Note that the standard library provides a specialization of the std::unique_ptr
template for array types, std::unique_ptr<T[]>, and corresponding overloads for
std::make_unique. Usage of these features is conforming to this rule.
Note that corresponding features for std::shared_ptr are only available in C++17
(usage of std::shared_ptr<T[]> with C++11 and C++14 will lead to compilation
errors). The overloads for std::make_shared will only be available in C++20.
Furthermore, note that it is possible to create a smart pointer of single object type
with a custom deleter handling an array of objects. This is well behaving as long as
this smart pointer is actually managing an array of objects. However, such a use is
error-prone, since the smart pointer can be assigned a single object again in the
reset function; it may no longer be possible in C++17 (moving a std::unique_ptr<T[]>
into a std::shared_ptr
Example
// $Id: A18-1-4.cpp 313638 2018-03-26 15:34:51Z jan.babst $
#include <memory>
class A
{
};
void F1()
{
// Create a dynamically allocated array of 10 objects of type A.
auto up1 = std::make_unique<A[]>(10); // Compliant
std::unique_ptr<A> up2{up1.release()}; // Non-compliant
}
void F2()
{
auto up1 = std::make_unique<A[]>(10); // Compliant
std::unique_ptr<A> up2;
up2.reset(up1.release()); // Non-compliant
}
void F3()
{
auto up = std::make_unique<A[]>(10); // Compliant
std::shared_ptr<A> sp{up.release()}; // Non-compliant
}
void F4()
{
auto up = std::make_unique<A[]>(10); // Compliant
std::shared_ptr<A> sp;
sp.reset(up.release()); // Non-compliant
}
void F5()
{
auto up = std::make_unique<A[]>(10); // Compliant
// sp will obtain its deleter from up, so the array will be correctly
// deallocated. However, this is no longer allowed in C++17.
std::shared_ptr<A> sp{std::move(up)}; // Non-compliant
sp.reset(new A{});
// leads to undefined behavior
}
void F6()
{
auto up = std::make_unique<A[]>(10); // Compliant
// Well behaving, but error-prone
std::shared_ptr<A> sp{up.release(),
std::default_delete<A[]>{}};
sp.reset(new A{}); // leads to undefined behavior
}
// Non-compliant
See also
HIC++ v4.0 [9]: 17.3.4: Do not create smart pointers of array type. ISO/IEC 14882:2014 [3]: 20.8 Smart pointers: [smartptr] Rule A18-5-3 in section 6.18.5