web-dev-qa-db-fra.com

Utiliser std :: move avec std :: shared_ptr

J'ai une fonction définie comme suit:

void foo(std::shared_ptr<X> x) { ... };

Si je déclare un ptr partagé à X:

std::shared_ptr<X> sourcePtr(new X(...));

Je peux alors appeler foo comme suit:

foo(std::move(sourcePtr));

ou

foo(sourcePtr);

Je comprends que si j'utilise la première option, sourcePtr devient nul. Cela empêche-t-il également le nombre de références d'être incrémenté?

Si cela n'a pas d'importance, quelle option dois-je préférer? Dois-je envisager autre chose pour prendre une telle décision?

27
ksl

Oui, si vous déplacez le pointeur partagé dans la fonction, alors:

  1. le sourcePtr d'origine deviendra nul et

  2. le nombre de références n'est pas modifié.

Si vous savez que vous n'aurez plus besoin de la valeur de sourcePtr après l'appel de fonction, le déplacer dans la fonction est une légère optimisation, car il enregistre un atomic incrémentation (et décrémentation ultérieure, lorsque sourcePtr sort du champ d'application).

Cependant, veillez à ce que l'identifiant sourcePtr soit toujours valide pour le reste de la portée, juste qu'il contient un pointeur nul. Ce qui signifie que le compilateur ne se plaindra pas si vous l'utilisez après le déplacement, mais si vous oubliez qu'il a été déplacé, vous déréférerez très probablement le null. J'ai tendance à utiliser cette "optimisation" move beaucoup, et j'ai également été mordu à plusieurs reprises: plus de fonctionnalités sont ajoutées à la fonction, et si vous oubliez d'annuler la move, vous obtenez un joli crash.

Donc, bouger lorsque vous n'en avez plus besoin est une légère optimisation associée à une légère charge de maintenance. C'est à vous de peser ce qui est plus important dans votre cas.

Ce qui précède suppose qu'il existe du code qui utilise réellement sourcePtr entre sa déclaration et l'appel final à foo (merci à @WhozCraig de l'avoir signalé). Si ce n'est pas le cas, il serait beaucoup préférable de créer le pointeur directement sur le site de l'appel:

foo(std::make_shared<X>(...));

De cette façon, vous enregistrez la même quantité d'opérations atomiques, et vous n'avez pas de pointeur partagé vide potentiellement dangereux qui traîne.

37
Angew