7
votes

Comment passer un partage_ptr sur un objet mutable en tant que paramètre?

Je veux transmettre un objet par référence intelligente-pointeur à une fonction. La fonction peut modifier la valeur de l'objet référencé, mais peut ne pas changer la référence elle-même. Il y a deux moyens évidents de gérer cela.

La première approche pour réussir le partage_ptr par valeur - IT est la référence, elle n'a donc pas besoin d'être adoptée par référence. La question apparente avec ceci est la copie de la référence, qui suggère une surcharge de comptage de référence. xxx

La deuxième approche consiste à transmettre le partage_ptr par const Référence - en évitant la copie de la copie L'instance Shared_Ptr, mais impliquant plutôt que des accès à l'objet référencé nécessitent deux couches de déséroférance au lieu d'un. xxx

dans la pratique, ces frais généraux théoriques seront normalement trivial et non pertinent. Ce qui me suggère que, au lieu de choisir une approche ou l'autre pour chaque cas individuel, je devrais presque toujours suivre une convention standard. Qui conduit à la question ...

existe-t-il une convention standard pour laquelle de ces approches que je devrais choisir normalement? Si tel est le cas, quel est le choix classique?

EDIT - Vaut probablement la peine d'être mentionné - une des raisons d'envisager l'affaire Pass-Be-Référence est que la convention préexistante est dû au fait qu'il existe une convention préexistante Que la plupart des instances de classe / structure sont transmises par Const-référence plutôt que par la valeur, et partagé_ptr est une classe. Bien sûr, ce n'est pas une classe des poids lourds (le coût de la copie est petit), de sorte que les raisons de cette ancienne convention ne peuvent pas s'appliquer.


5 commentaires

J'ai raté un duplicate éventuel avant de poster - Stackoverflow.com/Questtions/5330793/... - mais cela peut être plus spécifique à une exigence particulière plutôt qu'à une convention générale.


¤ Il y a des micro-inefficiences possibles de toute façon, alors allez pour clarté . Passer par la valeur. C'est ce que vous feriez pour un pointeur brut, c'est-à-dire la convention établie. Quelque chose d'autre fait un lecteur se demander pourquoi. Qui serait une mauvaise communication, pas clair. Acclamations et hth.,


@Alf - J'ai pensé à ce que vous avez dit. Une réponse est mon édition, mais cela ne veut pas dire que je suis en désaccord. Une référence brute ne peut pas être transmise par référence aussi (bien qu'un pointeur brut peut), donc je ne suis pas sûr que "la convention" est le mot juste, mais une pratique obligatoire est certainement conventionnelle. Je pense que vous avez un point, mais j'ai toujours des doutes - en C ++, lorsque les instances de réussite de la structure / classe de la constance sont si couramment faites (pour des raisons d'efficacité, mais aussi comme une convention), cela implique donc quelque chose à propos de l'intention? Peut-être que cela peut, mais probablement pas de manière claire / définitive.


@ Alfp.steinbach " Il y a des micro-inefficiences possibles quand même " avez-vous mesuré ces?


@CuciousGuy: Il m'est impossible de mesurer le code de l'OP. On a une surcharge de configuration fixe possible, l'autre solution a une éventuelle générale par accès. Le comportement dépend donc à la fois de laquelle de ces possibilités (le cas échéant) se manifester et de la quantité d'accès à chaque appel de fonction. acclamations et hth.,


4 Réponses :


13
votes

passe toujours partagé_ptr code> S par valeur. Si vous transmettez une référence, vous pouvez rencontrer le problème qu'un appel à une fonction de l'objet géré par le partagé_ptr code> pourrait simplement le réinitialiser et que vous avez maintenant un pointeur pendant. Si vous passez à la valeur, vous vous assurez que l'objet survivra à l'appel de fonction actuel.

voir ici pour beaucoup, beaucoup plus d'informations. p>


Exemple: p>

#include <memory>
#include <iostream>

std::shared_ptr<int> ptr(new int(42));

void foo(){
  ptr.reset();
}

void bar(std::shared_ptr<int> const& p){
  foo();
  std::cout << *p;
}

int main(){
  bar(ptr);
}


19 commentaires

Comment un appel à une fonction de l'objet géré par le pointeur réinitialisait-il le pointeur ou avoir un effet sur le pointeur? Ou je ne lis pas ça juste?


Peut-être un exemple? Comment et qu'est-ce que l'objet est réinitialisé?


@Benjamin: un objet qui est toujours géré par un partagé_ptr (héritage de activer_shared_from_this etc.) pourrait rappeler au parent qui le gère et qui pourrait le réinitialiser .. quelque chose comme ca. Il n'y avait pas trop longtemps que je lisais le texte où cette raison exacte d'une bonne explication a été donnée, si seulement je pouvais le trouver ...


@Benjamin Lindley Vector RealLocation vectorielle, réaffectation de chaîne, invalidation des références, ect ...


@Benjamin, Onclebens: Trouvé ça! :RÉ


Bonne question. Dans le cas Pass-By-Référence, le Shared_PTR est adopté par Const référence, que je m'attendrais à assurer une conscience au moins logique sinon une immuabilité complète. S'il s'agit d'un vrai problème, j'aimerais voir un exemple et une explication détaillée.


@Steve: Juste parce que le pointeur ne peut pas être changé à travers cette référence ne signifie pas que cela ne peut pas être changé de l'extérieur.


@Xeo: Je suis d'accord avec la valeur toujours pass par la valeur. Mais lorsque l'argument formel fait référence à Cons, alors aucun mal ne peut en arriver (c'est-à-dire autre que l'aliasing possible d'un aliasing possible pour un argument de Ref-to-const, que j'ai jamais rencontré, et qui s'applique également à toute utilisation de l'argument formel Ref-to-Const). seulement une micro-inefficacité possible. à votre santé,


@Xeo - a accepté, mais c'est un problème de référence standard par conscrome qui n'inquiète normalement personne. Si le multi-threading est un problème, c'est le moins de vos problèmes. Si vous enregistrez une copie de cette référence, en supposant que cela signifie copier l'instance Shared_Ptr, vous devriez être correct. Sinon, le problème ne peut pas survenir - rien à l'extérieur de la fonction permet de modifier la référence pendant que vous l'utilisez, car rien en dehors de la fonction ne doit exécuter que lorsque la fonction se décède et que le paramètre meurt de toute façon.


@Steve: Eh bien, vous pouvez appeler des fonctions qui tuent le partagé_ptr à partir de votre fonction.


@Xeo - Non, car il s'agit d'une référence const à un partagé_ptr . OK, techniquement, il y a des moyens de jeter cette constance, mais si les gens le font juste pour le plaisir, la source de votre problème est pas la référence PASS-BY-CONSTRISE. Les gens pourraient par exemple. Mémoire corrompue au hasard pour le plaisir, modifiant les valeurs de partagée_ptr des instances (et d'autres choses) copiées. Aucun de cela signifie que le passage de la const-référence est la bonne convention - à ce stade, je me penche fortement vers la valeur transvenant - mais j'ai des doutes sur la justification.


@Steve: Vous ne semblez pas comprendre ce que je veux dire. Je vais éditer dans un exemple artificiel, alors portez-moi avec moi pendant une seconde. Edit: Là, fait.


@Xeo: Les exemples impliquant des variables globales ne sont pas très pertinents, comme si vous utilisez une variable globale, alors vous mendez de tels problèmes - cela n'est rien spécifique à partagé_ptr .


@Deadmg: J'ai dit que c'était un exemple artificiel. Maintenant, il suffit de mettre le partagé_ptr et foo dans une classe et bar dans un autre ...


@Xeo: Voici un exemple artificiel indiquant que vous ne devez pas passer les bools par référence. Ideone.com/1iyyc


@Xeo - Bon point - Je suis d'accord avec DeadMg sauf que cela est à peu près possible que cela se produise par accident. Je sais que parce que je l'ai immédiatement reconnu comme une variante simplifiée de quelque chose que j'ai fait - pas avec Shared_ptr (ce que j'ai manifestement utilisé rarement jusqu'à présent dans la pratique), mais pour d'autres types. Vous avez un conteneur. Vous recherchez le conteneur pour une "valeur", que vous référence de const plutôt que de copier. Vous appelez une autre opération qui mutte le même conteneur - mutating ou déplaçant la valeur référencée. OMS! Rare et a averti explicitement par ex. Pour les conteneurs STL, mais vous obtenez l'acceptation.


Ainsi, vous êtes opposé à la transe-const-référence dans tous les cas? Qu'en est-il des références de non-const? Qu'en est-il des pointeurs? Qu'en est-il de ceci ?


@Cuciousguy: pour partagé_ptr ? Oui. En général? Non. Avez-vous même lu le lien que j'ai fourni? C'est la première phrase pourquoi partagé_ptr doit être transmis par la valeur. Il ne s'applique pas à d'autres choses, car ils n'ont pas la même sémitique que partagé_ptr .


Donc, vous êtes opposé à la transe-const-référence pour vecteur ? Qu'en est-il des références de non-const? Qu'en est-il des pointeurs?



-1
votes

Un exemple sur ce que @xeo a dit: xxx

ceci ne se produit pas pour const partage_ptr & p .


6 commentaires

Je pense que vous êtes manquant un p.Reset () ou quelque chose comme ça?


Shared_Ptr est const à l'intérieur de la fonction, vous ne pouvez pas appeler réinitialiser dessus.


Pour plus de clarté - ce que Cat Plus plus a dit est vrai des exemples de ma question originale - les alternatives d'origine n'incluaient pas Shared_ptr & p , ils étaient partage_ptr p et (le correspondant) const partage_ptr & p .


Cela devient encore plus ridicule. Pourquoi sinon passeriez-vous une référence non-Const si vous n'avez pas l'intention de modifier l'argument?


@Unclebens - La question initiale n'a pas suggéré de transmettre une référence non constituée du tout. Si vous dites que cette réponse fait quelque chose qui est parfaitement raisonnable, je suis d'accord - il n'y a pas d'interdiction des fonctions ayant des effets secondaires en C ou en C ++, et en passant par une référence non-Const communique l'intention de modifier l'objet référencé ( Dans ce cas, le partagée_ptr instance), de sorte que cet effet "effet secondaire" est un effet intentionnel parfaitement raisonnable.


Ce que @Xeo a à l'esprit est quelque chose de similaire au problème de l'auto-affectation. Si un opérateur d'affectation surchargé est implémenté comme Supprimer * m_ptr; m_ptr = nouvel objet (rhv.m_ptr); , il va exploser quand this == & rhv . - Il doit y avoir un autre moyen d'accéder à une référence non-const au même partage_ptr.



0
votes

Si la fonction manipule l'objet, il ne doit pas être pertinent si l'objet est contenu dans un pointeur intelligent. Il devrait accepter un t & . Des fonctions libres qui manipulent des valeurs sont considérées comme un style mauvais par certains. Prenant l'objet en référence à Const et retourner une nouvelle valeur peut être plus propre.


0 commentaires

0
votes

Je passe à Const & basé sur l'idée que quelque part dans la pile d'appels, quelqu'un devrait tenir une copie du partage_ptr et que sa vie est garantie pour toute mon exécution.

Dans le cas où vous exécutez sur un thread différent, vous avez besoin d'une copie - et de cette question que quiconque stocke le partage_ptr (non seulement l'utilise pour la durée de leur méthode) doit Stockez une copie, pas une référence.

Et, maintenant que j'ai écrit que je vais lire pourquoi les gens semblent soutenir la mentalité opposée.


0 commentaires