Si je déclare un objet enveloppé dans un pointeur partagé:
std::shared_ptr<myClass> myClassObject(new myClass());
alors j'ai voulu le passer comme argument à une méthode:
DoSomething(myClassObject);
//the called method
void DoSomething(std::shared_ptr<myClass> arg1)
{
arg1->someField = 4;
}
Est-ce que ce qui précède incrémente simplement le nombre de références de shared_pt et tout est cool? Ou laisse-t-il un pointeur suspendu?
Êtes-vous toujours censé le faire?:
DoSomething(myClassObject.Get());
void DoSomething(std::shared_ptr<myClass>* arg1)
{
(*arg1)->someField = 4;
}
Je pense que la 2ème manière peut être plus efficace car elle n'a qu'à copier 1 adresse (par opposition à l'ensemble du pointeur intelligent), mais la 1ère manière semble plus lisible et je ne prévois pas de repousser les limites de performances. Je veux juste m'assurer qu'il n'y a pas quelque chose de dangereux à ce sujet.
Merci.
c++
c++11
shared-ptr
c++-faq
Steve H
la source
la source
const std::shared_ptr<myClass>& arg1
DoSomething
vraiment partager la propriété? Il semble que cela devrait simplement prendre une référence à la place ...void DoSomething(myClass& arg1)
.shared_ptr<>
particulier, vous devez passer par valeur pour réellement partager la propriété.std::make_shared
est non seulement plus efficace, mais aussi plus sûr que lestd::shared_ptr
constructeur.Réponses:
Bien sûr, je peux vous aider. Je suppose que vous avez une certaine compréhension de la sémantique de propriété en C ++. Est-ce vrai?
Bien.
Ok, je ne peux penser qu'à deux raisons de prendre un
shared_ptr
argument:shared_ptr
s.Laquelle vous intéresse?
Des exemples de telles fonctions incluent
std::static_pointer_cast
des comparateurs personnalisés ou des prédicats. Par exemple, si vous avez besoin de trouver tous les shared_ptr uniques à partir d'un vecteur, vous avez besoin d'un tel prédicat.Exactement.
Oui. Et si cela ne change pas le pointeur, vous voulez passer par référence const. Il n'est pas nécessaire de copier puisque vous n'avez pas besoin de partager la propriété. C'est l'autre scénario.
Celui dont vous partagez la propriété? D'accord. Avec quoi partagez-vous la propriété
shared_ptr
?Ensuite, la fonction devra faire une copie d'un
shared_ptr
, correct?Non, c'est une pessimisation. S'il est passé par référence, la fonction n'aura d'autre choix que de faire la copie manuellement. S'il est passé par valeur, le compilateur choisira le meilleur choix entre une copie et un déplacement et l'exécutera automatiquement. Alors, passez par valeur.
La fonction peut simplement déplacer l'
shared_ptr
argument dans son stockage. Déplacer unshared_ptr
est bon marché car cela ne change aucun nombre de références.Dans ce cas, cela
shared_ptr
n'a aucun rapport avec la fonction. Si vous souhaitez manipuler la pointee, prenez une pointee et laissez les appelants choisir la sémantique de propriété qu'ils souhaitent.Les règles habituelles s'appliquent. Les pointeurs intelligents ne changent rien.
Droite.
Ah, un cas de pointe intéressant. Je ne m'attends pas à ce que cela arrive souvent. Mais quand cela se produit, vous pouvez soit passer par valeur et ignorer la copie si vous n'en avez pas besoin, soit passer par référence et faire la copie si vous en avez besoin.
Si vous êtes dans une situation où cela compte vraiment, vous pouvez fournir deux surcharges, l'une prenant une référence const lvalue et l'autre prenant une référence rvalue. L'un copie, l'autre bouge. Un modèle de fonction de transfert parfait est une autre option.
la source
shared_ptr<T> ptr;
(c'estvoid f(T&)
-à- dire appelé avecf(*ptr)
) et l'objet ne survit pas à l'appel. Sinon, écrivez-en un où vous passez par valeurvoid f(T)
et où des problèmes surviennent.void f(T&)
et implique des threads. Je pense que mon problème est que le ton de votre réponse décourage de passer la propriété de shared_ptr, qui est la manière la plus sûre, la plus intuitive et la moins compliquée de les utiliser. Je serais extrêmement contre de conseiller jamais à un débutant d'extraire une référence à des données appartenant à un shared_ptr <> les possibilités d'utilisation abusive sont grandes et l'avantage est minime.Je pense que les gens ont inutilement peur d'utiliser des pointeurs bruts comme paramètres de fonction. Si la fonction ne va pas stocker le pointeur ou affecter sa durée de vie, un pointeur brut fonctionne tout aussi bien et représente le plus petit dénominateur commun. Considérez par exemple comment vous passeriez a
unique_ptr
dans une fonction qui prend ashared_ptr
comme paramètre, soit par valeur, soit par référence const?void DoSomething(myClass * p); DoSomething(myClass_shared_ptr.get()); DoSomething(myClass_unique_ptr.get());
Un pointeur brut en tant que paramètre de fonction ne vous empêche pas d'utiliser des pointeurs intelligents dans le code appelant, là où cela compte vraiment.
la source
DoSomething(*a_smart_ptr)
fun(&x)
àfun(x)
. Dans ce dernier exemple, ilx
peut être passé par valeur ou par const ref. Comme indiqué ci-dessus, cela vous permettra également de passernullptr
si vous n'êtes pas intéressé par la sortie ...Oui, l'idée même d'un shared_ptr <> est que plusieurs instances peuvent contenir le même pointeur brut et la mémoire sous-jacente ne sera libérée que lorsque la dernière instance de shared_ptr <> sera détruite.
J'éviterais un pointeur vers un shared_ptr <> car cela va à l'encontre de l'objectif car vous avez à nouveau affaire à raw_pointers.
la source
Le passage par valeur dans votre premier exemple est sûr, mais il y a un meilleur idiome. Passez par référence const lorsque cela est possible - je dirais oui même lorsque vous traitez avec des pointeurs intelligents. Votre deuxième exemple n'est pas exactement cassé mais il est très
!???
. Idiot, ne rien accomplir et vaincre une partie de l'intérêt des pointeurs intelligents, et vous laisser dans un monde bogué de douleur lorsque vous essayez de déréférencer et de modifier les choses.la source
shared_ptr<>
qui concerne spécifiquement, que vous devez faire une copie afin de réellement partager la propriété; si vous avez une référence ou un pointeur vers a,shared_ptr<>
vous ne partagez rien et êtes soumis aux mêmes problèmes de durée de vie qu'une référence ou un pointeur normal.shared_ptr
de l'appelant est garanti pour durer plus longtemps que l'appel de fonction, donc la fonction est sûre d'utiliser leshared_ptr<> const &
. S'il a besoin de stocker le pointeur pour une date ultérieure, il devra copier (à ce stade, une copie doit être faite, plutôt que de contenir une référence), mais il n'est pas nécessaire d'engager le coût de la copie sauf si vous devez le faire il.shared_ptr
de l'interface et passer uneconst
référence plain ( ) à l'objet pointé.shared_ptr<>
au départ? Maintenant, votre API n'est vraiment qu'une douleur dans le cou, car elle oblige l'appelant à changer inutilement la sémantique de la durée de vie de l'objet juste pour l'utiliser. Je ne vois rien qui vaille la peine d'être préconisé à cet égard, à part de dire "techniquement, cela ne nuit à rien". Je ne vois rien de controversé à propos de «si vous n'avez pas besoin de propriété partagée, ne l'utilisez passhared_ptr<>
».dans votre fonction,
DoSomething
vous modifiez un membre de données d'une instance de classe demyClass
sorte que ce que vous modifiez est l'objet géré (pointeur brut) et non l'objet (shared_ptr). Ce qui signifie qu'au point de retour de cette fonction, tous les pointeurs partagés vers le pointeur brut géré verront leur membre de données:myClass::someField
changé en une valeur différente.dans ce cas, vous passez un objet à une fonction avec la garantie que vous ne le modifiez pas (en parlant de shared_ptr et non de l'objet possédé).
L'idiome pour exprimer cela est à travers: un const ref, comme ça
void DoSomething(const std::shared_ptr<myClass>& arg)
De même, vous assurez à l'utilisateur de votre fonction que vous n'ajoutez pas un autre propriétaire à la liste des propriétaires du pointeur brut. Cependant, vous avez conservé la possibilité de modifier l'objet sous-jacent pointé par le pointeur brut.
CAVEAT: Ce qui signifie que si, d'une manière ou d'une autre, quelqu'un appelle
shared_ptr::reset
avant d'appeler votre fonction, et à ce moment-là, c'est le dernier shared_ptr possédant le raw_ptr, alors votre objet sera détruit et votre fonction manipulera un pointeur pendant pour détruire l'objet. TRÈS DANGEREUX!!!la source