tshah06 tshah06 - 1 month ago 9
C++ Question

shared_ptr to an array : should it be used?

Just a small query regarding

.

Is it a good practice to use
shared_ptr
pointing to an array? For example,

shared_ptr<int> sp(new int[10]);


If not, then why not? One reason I am already aware of is one can not increment/decrement the
shared_ptr
. Hence it can not be used like a normal pointer to an array.

Answer

By default, shared_ptr will call delete on the managed object when no more references remain to it. However, when you allocate using new[] you need to call delete[], and not delete, to free the resource.

In order to correctly use shared_ptr with an array, you must supply a custom deleter.

template< typename T >
struct array_deleter
{
  void operator ()( T const * p)
  { 
    delete[] p; 
  }
};

Create the shared_ptr as follows:

std::shared_ptr<int> sp( new int[10], array_deleter<int>() );

Now shared_ptr will correctly call delete[] when destroying the managed object.


As shown in the comments, with C++11, you can use the std::default_delete partial specialization for array types instead of array_deleter above.

std::shared_ptr<int> sp( new int[10], std::default_delete<int[]>() );

You can also use a lambda expression instead of functors.

std::shared_ptr<int> sp( new int[10], []( int *p ) { delete[] p; } );

Also, unless you actually need to share the managed object, a unique_ptr is better suited for this task, since it has a partial specialization for array types.

std::unique_ptr<int[]> up( new int[10] ); // this will correctly call delete[]


Changes introduced by the C++ Extensions for Library Fundamentals

shared_ptr is being augmented by the Library Fundamentals Technical Specification (TS) to allow it to work out of the box for the cases when it owns an array of objects; there'll be no need to explicitly supply a deleter. The current draft of the shared_ptr changes slated for this TS can be found in N4082. These changes will be accessible via the std::experimental namespace, and included in the <experimental/memory> header. A few of the relevant changes to support shared_ptr for arrays are:

— The definition of the member type element_type changes

typedef T element_type;

 typedef typename remove_extent<T>::type element_type;

— Member operator[] is being added

 element_type& operator[](ptrdiff_t i) const noexcept;

— Unlike the unique_ptr partial specialization for arrays, both shared_ptr<T[]> and shared_ptr<T[N]> will be valid and both will result in delete[] being called on the managed array of objects.

 template<class Y> explicit shared_ptr(Y* p);

Requires: Y shall be a complete type. The expression delete[] p, when T is an array type, or delete p, when T is not an array type, shall be well-formed, shall have well defined behavior, and shall not throw exceptions. When T is U[N], Y(*)[N] shall be convertible to T*; when T is U[], Y(*)[] shall be convertible to T*; otherwise, Y* shall be convertible to T*.