web-dev-qa-db-fra.com

Passer des pointeurs partagés comme arguments

Si je déclare un objet encapsulé dans un pointeur partagé:

std::shared_ptr<myClass> myClassObject(new myClass());

alors je voulais 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 simplement incrémente le nombre de références de shared_pt et que tout est cool? Ou laisse-t-il un pointeur en suspens?

Êtes-vous toujours supposé faire ça ?:

DoSomething(myClassObject.Get());

void DoSomething(std::shared_ptr<myClass>* arg1)
{
   (*arg1)->someField = 4;
}

Je pense que la 2ème voie est peut-être plus efficace car elle ne doit copier qu'une adresse (par opposition à l'intégralité du pointeur intelligent), mais la 1ère voie 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 de danger.

Merci.

79
Steve H

Je veux passer un pointeur partagé à une fonction. Pouvez-vous m'aider?

Bien sûr, je peux vous aider avec ça. Je suppose que vous avez une certaine compréhension de la sémantique de la propriété en C++. Est-ce vrai?

Oui, je suis assez à l'aise avec le sujet.

Bien.

Ok, je ne peux penser qu'à deux raisons pour prendre un shared_ptr argument:

  1. La fonction veut partager la propriété de l'objet.
  2. La fonction effectue une opération qui fonctionne spécifiquement sur shared_ptrs.

Lequel êtes-vous intéressé?

Je cherche une réponse générale, je suis donc intéressé par les deux. Je suis curieux de savoir ce que vous voulez dire dans le cas n ° 2, cependant.

std::static_pointer_cast, comparateurs personnalisés ou prédicats. Par exemple, si vous devez rechercher tous les shared_ptr uniques à partir d'un vecteur, vous avez besoin d'un tel prédicat.

Ah, quand la fonction doit réellement manipuler le pointeur intelligent lui-même.

Exactement.

Dans ce cas, je pense que nous devrions passer par référence.

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.

Ok, j'ai compris. Parlons de l'autre scénario.

Celui où vous partagez la propriété? D'accord. Comment partagez-vous la propriété avec shared_ptr?

En le copiant.

Ensuite, la fonction devra faire une copie d'un shared_ptr, correct?

Évidemment. Donc, je le passe par une référence à const et copier dans une variable locale?

Non, c'est une pessimisation. S'il est passé par référence, la fonction n'aura pas 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.

Bon point. Je dois me rappeler que " Want Speed? Passe par Valeur. " article plus souvent.

Attendez, si la fonction stocke le shared_ptr dans une variable membre, par exemple? Cela ne fera-t-il pas une copie redondante?

La fonction peut simplement déplacer le shared_ptr argument dans son stockage. Déplacer un shared_ptr n'est pas cher, car cela ne change en rien le nombre de références.

Ah, bonne idée.

Mais je pense à un troisième scénario: et si vous ne voulez pas manipuler le shared_ptr, ni pour partager la propriété?

Dans ce cas, shared_ptr n'a aucun rapport avec la fonction. Si vous voulez manipuler la pointee, prenez-en une et laissez les appelants choisir la sémantique de propriété qu'ils souhaitent.

Et devrais-je prendre la pointee par référence ou par valeur?

Les règles habituelles s'appliquent. Les pointeurs intelligents ne changent rien.

Passe par valeur si je vais copier, passe par référence si je veux éviter une copie.

Droite.

Hmm. Je pense que vous avez encore oublié un autre scénario. Et si je veux partager la propriété, mais uniquement en fonction d'une certaine condition?

Ah, une affaire Edge intéressante. Je ne m'attends pas à ce que cela se produise 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 créer la copie si vous en avez besoin.

Je risque une copie redondante dans la première option et perds un coup potentiel dans la seconde. Je ne peux pas manger le gâteau et l'avoir aussi?

Si vous êtes dans une situation où cela compte vraiment, vous pouvez fournir deux surcharges, une en prenant une référence constante, et une autre en prenant une référence réelle. Une copie, l'autre bouge. Un modèle de fonction de transfert parfait est une autre option.

Je pense que cela couvre tous les scénarios possibles. Merci beaucoup.

154

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 autrement 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 un unique_ptr dans une fonction qui prend un shared_ptr en tant que paramètre, par valeur ou 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 d'appel, là où cela compte vraiment.

17
Mark Ransom

Oui, l'idée d'un shared_ptr <> réside dans le fait que plusieurs instances peuvent contenir le même pointeur brut et que 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 sur un shared_ptr <>, car cela irait à l'encontre de l'objectif car vous avez à nouveau affaire à raw_pointers.

3

Passer par valeur dans votre premier exemple est sûr, mais l'idiome est meilleur. Passez par référence lorsque cela est possible - je dirais que oui, même avec des pointeurs intelligents. Votre deuxième exemple n’est pas exactement cassé mais c’est très !???. Stupide, ne rien accomplir et défait une partie du sens des indicateurs intelligents, et va vous laisser dans un monde de chagrin de douleur lorsque vous essayez de déréférencer et de modifier les choses.

2
djechlin