Passer unique_ptr aux fonctions

J’essaie de “moderniser” un code existant.

  • J’ai une classe qui a actuellement une variable membre “Device * device_”.
  • Il utilise new pour créer une instance dans un code d’initialisation et possède un “delete device_” dans le destructory.
  • Les fonctions membres de cette classe appellent beaucoup d’ autres fonctions qui prennent un périphérique * comme paramètre.

Cela fonctionne bien, mais pour “moderniser” mon code, je pensais devoir changer la variable à définir comme "std::unique_ptr device_" et supprimer l’appel explicite à supprimer, ce qui rend le code plus sûr et généralement meilleur.

Ma question est la suivante –

  • Comment dois-je ensuite passer la variable device _ à toutes les fonctions qui en ont besoin en tant que paramètre?

Je peux appeler .get pour obtenir le pointeur brut dans chaque appel de fonction. Mais cela semble moche et gaspille certaines des raisons d’utiliser un unique_ptr en premier lieu.

Ou je peux changer chaque fonction pour qu’au lieu de prendre un paramètre de type “Device *”, il prenne maintenant un paramètre de type “std :: unique_ptr &”. Ce qui (pour moi) obscurcit quelque peu les prototypes de fonctions et les rend difficiles à lire.

Quelle est la meilleure pratique pour cela? Ai-je manqué d’autres options?

Dans le style C ++ moderne, il y a deux concepts clés:

  • La possession
  • Nullité

La propriété concerne le propriétaire de certains objects / ressources (dans ce cas, une instance de Device ). Les différents std::unique_ptr , boost::scoped_ptr ou std::shared_ptr concernent la propriété.

La nullité est beaucoup plus simple cependant: elle exprime simplement la question de savoir si un object donné peut être nul ou non, et ne se soucie pas de tout, et certainement pas de la propriété!


Vous avez eu raison de déplacer l’implémentation de votre classe vers unique_ptr (en général), bien que vous souhaitiez un pointeur intelligent avec une sémantique de copie profonde si votre objective est d’implémenter une PIMPL.

Cela montre clairement que votre classe est la seule responsable de cet élément de mémoire et traite de manière nette de toutes les différentes manières dont la mémoire aurait pu s’échapper autrement.


D’un autre côté, la plupart des utilisateurs des ressources ne se préoccupent pas de leur propriété.

Tant qu’une fonction ne garde pas de référence à un object (stockez-la dans une carte ou autre), tout ce qui compte, c’est que la durée de vie de l’object dépasse la durée de l’appel de fonction.

Ainsi, choisir comment passer le paramètre dépend de sa nullité possible:

  • Jamais nul? Passer une référence
  • Peut-être nul? Passer un pointeur , un simple pointeur nu ou une classe de type pointeur (avec un piège sur null par exemple)

Cela dépend vraiment. Si une fonction doit prendre possession de l’unique_ptr, alors sa signature devrait prendre une valeur unique_ptr bv et l’appelant devrait std::move le pointeur. Si la propriété n’est pas un problème, alors je conserverais la signature du pointeur brut et transmettrais le pointeur unique_ptr en utilisant get() . Ce n’est pas moche si la fonction en question ne prend pas en charge la propriété.

Je voudrais utiliser std::unique_ptr const& . L’utilisation d’une référence non const permet à la fonction appelée de réinitialiser votre pointeur.
Je pense que c’est un bon moyen d’exprimer que votre fonction appelée peut utiliser le pointeur mais rien d’autre.
Donc, pour moi, cela rendra l’interface plus facile à lire. Je sais que je n’ai pas à fouiller avec le pointeur passé à moi.

La meilleure pratique est probablement de ne pas utiliser std::unique_ptr dans ce cas, même si cela dépend. (Vous ne devriez généralement pas avoir plus d’un pointeur brut sur un object alloué dynamicment dans une classe. Bien que cela dépend aussi.) La seule chose que vous ne voulez pas faire dans ce cas est de transmettre std::unique_ptr (et comme vous avez remarqué, std::unique_ptr<> const& est un peu lourd et obscur). S’il s’agit du seul pointeur alloué dynamicment dans l’object, je m’en tiendrai au pointeur brut et à la delete dans le destructeur. S’il y a plusieurs pointeurs de ce type, j’envisagerais de les reléguer dans une classe de base distincte (où ils peuvent toujours être des pointeurs bruts).

Cela peut ne pas être réalisable pour vous, mais remplacer chaque occurrence de Device* par const unique_ptr& est un bon début.

Vous ne pouvez évidemment pas copier unique_ptr et vous ne voulez pas le déplacer. Le remplacement par une référence à unique_ptr permet au corps des fonctions existantes de continuer à fonctionner.

Maintenant, il y a un piège, vous devez passer par const & pour empêcher les calle de faire unique_ptr.reset() ou unique_ptr().release() . Notez que cela passe toujours un pointeur modifiable sur le périphérique. Avec cette solution, vous n’avez pas de moyen facile de transmettre un pointeur ou une référence à un const Device .