web-dev-qa-db-fra.com

shared_ptr à un tableau: doit-il être utilisé?

Juste une petite question concernant shared_ptr .

Est-ce une bonne pratique d’utiliser _shared_ptr_ en pointant vers un tableau? Par exemple,

_shared_ptr<int> sp(new int[10]);
_

Si non, alors pourquoi pas? Une des raisons que je connais déjà est qu’on ne peut pas incrémenter/décrémenter le _shared_ptr_. Par conséquent, il ne peut pas être utilisé comme un pointeur normal vers un tableau.

154
tshah06

Avec C++ 17 , _shared_ptr_ peut être utilisé pour gérer un tableau alloué de manière dynamique. L'argument de modèle _shared_ptr_ dans ce cas doit être _T[N]_ ou _T[]_. Donc, vous pouvez écrire

_shared_ptr<int[]> sp(new int[10]);
_

À partir de n4659, [util.smartptr.shared.const]

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

Nécessite: Y doit être un type complet. L'expression _delete[] p_, lorsque T est un type de tableau, ou _delete p_, lorsque T n'est pas un type de tableau, doit avoir un comportement bien défini et ne pas lever d'exceptions. .
...
Remarques: Lorsque T est un type de tableau, ce constructeur ne doit pas participer à la résolution de surcharge sauf si l'expression _delete[] p_ est bien formée. et soit T est _U[N]_ et Y(*)[N] est convertible en _T*_ ou T est _U[]_ et Y(*)[] est convertible en _T*_. ...

Pour supporter cela, le type de membre element_type est maintenant défini comme

_using element_type = remove_extent_t<T>;
_

Les éléments de tableau peuvent être utilisés avec operator[]

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

requiert: get() != 0 && i >= 0. Si T est _U[N]_, _i < N_. ...
Remarques: Lorsque T n'est pas un type de tableau, il n'est pas précisé si cette fonction membre est déclarée. Si elle est déclarée, le type de son retour n'est pas spécifié, sauf que la déclaration (bien que pas nécessairement la définition) de la fonction doit être bien formée.


Avant C++ 17 , _shared_ptr_ pourrait pas être utilisé pour gérer des tableaux alloués dynamiquement. Par défaut, _shared_ptr_ appellera delete sur l'objet géré lorsqu'il ne restera plus de références. Toutefois, lorsque vous allouez avec _new[]_, vous devez appeler _delete[]_ et non pas delete pour libérer la ressource.

Pour utiliser correctement _shared_ptr_ avec un tableau, vous devez fournir un deleter personnalisé.

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

Créez le shared_ptr comme suit:

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

Désormais, _shared_ptr_ appellera correctement _delete[]_ lors de la destruction de l'objet géré.

Le deleter personnalisé ci-dessus peut être remplacé par

  • la spécialisation partielle std::default_delete pour les types de tableaux

    _std::shared_ptr<int> sp(new int[10], std::default_delete<int[]>());
    _
  • une expression lambda

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

De même, à moins que vous n'ayez réellement besoin de partager l'authenticité de partage de l'objet géré, un _unique_ptr_ convient mieux à cette tâche, car il dispose d'une spécialisation partielle pour les types de tableau.

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

Modifications introduites par les extensions C++ pour les bases de la bibliothèque

Une autre solution d’avant C++ 17 à celles énumérées ci-dessus a été fournie par le Spécification technique des bases de la bibliothèque , qui a augmenté _shared_ptr_ pour lui permettre de fonctionner immédiatement avec les cas suivants: possède un tableau d'objets. Le brouillon actuel des modifications _shared_ptr_ prévues pour ce TS peut être trouvé dans N4082 . Ces modifications seront accessibles via l'espace de noms _std::experimental_ et incluses dans l'en-tête _<experimental/memory>_. Voici quelques modifications pertinentes à prendre en charge pour _shared_ptr_ pour les tableaux:

- La définition du type de membre _element_type_ change

typedef T type_élément;

_ typedef typename remove_extent<T>::type element_type;
_

- Le membre _operator[]_ est ajouté

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

- Contrairement à la spécialisation partielle _unique_ptr_ pour les tableaux, _shared_ptr<T[]>_ et _shared_ptr<T[N]>_ sont valides et les deux entraîneront l'appel de _delete[]_ sur le tableau d'objets géré.

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

Requiert : Y doit être un type complet. L'expression _delete[] p_, lorsque T est un type de tableau, ou _delete p_, lorsque T n'est pas un type de tableau, doit être correctement formé et doit avoir un comportement bien défini. et ne jettera pas d'exceptions. Lorsque T est _U[N]_, Y(*)[N] doit être convertible en _T*_; lorsque T est _U[]_, Y(*)[] doit être convertible en _T*_; sinon, _Y*_ doit pouvoir être converti en _T*_.

239
Praetorian

Une alternative peut-être plus facile à utiliser est shared_ptr<vector<int>>.

25
Timmmm