shared_ptr para uma matriz: deve ser usado?

Apenas uma pequena consulta sobre shared_ptr .

É uma boa prática usar shared_ptr apontando para uma matriz? Por exemplo,

 shared_ptr sp(new int[10]); 

Se não, então por que não? Uma razão pela qual já estou ciente é que não é possível incrementar / decrementar o shared_ptr . Por isso, não pode ser usado como um ponteiro normal para um array.

Com o C ++ 17 , o shared_ptr pode ser usado para gerenciar uma matriz alocada dinamicamente. O argumento do modelo shared_ptr neste caso deve ser T[N] ou T[] . Então você pode escrever

 shared_ptr sp(new int[10]); 

De n4659, [util.smartptr.shared.const]

  template explicit shared_ptr(Y* p); 

Requer: Y será um tipo completo. A expressão delete[] p , quando T é um tipo de array, ou delete p , quando T não é um tipo de array, deve ter um comportamento bem definido, e não deve lançar exceções.

Observações: Quando T é um tipo de matriz, este construtor não deve participar na resolução de sobrecarga a menos que a expressão delete[] p seja bem formada e T seja U[N] e Y(*)[N] seja convertível em T* , ou T é U[] e Y(*)[] é convertível em T* . …

Para suportar isso, o tipo de membro element_type é agora definido como

 using element_type = remove_extent_t; 

Elementos de matriz podem ser acessados ​​usando operator[]

  element_type& operator[](ptrdiff_t i) const; 

Requer: get() != 0 && i >= 0 . Se T é U[N] , i < N . ...
Observações: Quando T não é um tipo de matriz, não é especificado se essa function de membro é declarada. Se for declarado, não é especificado qual é o seu tipo de retorno, exceto que a declaração (embora não necessariamente a definição) da function deve ser bem formada.


Antes do C ++ 17 , o shared_ptr não podia ser usado para gerenciar matrizes alocadas dinamicamente. Por padrão, shared_ptr chamará delete no object gerenciado quando não houver mais referências a ele. No entanto, quando você aloca usando new[] você precisa chamar delete[] , e não delete , para liberar o recurso.

Para usar corretamente o shared_ptr com um array, você deve fornecer um deleter personalizado.

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

Crie o shared_ptr da seguinte maneira:

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

Agora shared_ptr irá chamar corretamente delete[] ao destruir o object gerenciado.

O apagador personalizado acima pode ser substituído por

  • a especialização parcial std::default_delete para tipos de matriz

     std::shared_ptr sp(new int[10], std::default_delete()); 
  • uma expressão lambda

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

Além disso, a menos que você realmente precise compartilhar onwership do object gerenciado, um unique_ptr é mais adequado para essa tarefa, pois possui uma especialização parcial para os tipos de matriz.

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

Alterações introduzidas pelas Extensões C ++ para Fundamentos da Biblioteca

Outra alternativa pré-C ++ 17 às listadas acima foi fornecida pela Especificação Técnica de Fundamentos da Biblioteca , que aumentou o shared_ptr para permitir que funcione fora da checkbox para os casos em que possui uma matriz de objects. O rascunho atual das mudanças shared_ptr programadas para este TS pode ser encontrado em N4082 . Essas alterações serão acessíveis através do namespace std::experimental e incluídas no header . Algumas das alterações relevantes para suportar shared_ptr para arrays são:

- A definição do tipo de membro element_type changes

typedef T element_type;

  typedef typename remove_extent::type element_type; 

- O operator[] membro operator[] está sendo adicionado

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

- Ao contrário da especialização parcial unique_ptr para matrizes, shared_ptr e shared_ptr serão válidos e ambos resultarão em delete[] sendo chamado na matriz gerenciada de objects.

  template explicit shared_ptr(Y* p); 

Requer : Y será um tipo completo. A expressão delete[] p , quando T é um tipo de matriz, ou delete p , quando T não é um tipo de array, deve ser bem formada, deve ter um comportamento bem definido e não deve lançar exceções. Quando T é U[N] , Y(*)[N] deve ser convertível em T* ; quando T é U[] , Y(*)[] deve ser convertível em T* ; caso contrário, Y* será convertível em T* .

Uma alternativa possivelmente mais fácil que você pode usar é shared_ptr> .