Le point distinct shared_ptr
de l'instance est de garantir autant que possible) que tant que ce shared_ptr
est dans la portée, l'objet qu'il points de, parce que son compte de référence sera d'au moins 1.
Class::only_work_with_sp(boost::shared_ptr<foo> sp)
{
// sp points to an object that cannot be destroyed during this function
}
Ainsi, en utilisant une référence à un shared_ptr
, vous désactivez la garantie. Donc, dans votre deuxième cas:
Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here
{
...
sp->do_something();
...
}
Comment savez-vous que sp->do_something()
ne va pas exploser en raison d'un pointeur null?
Tout dépend de ce qui est dans les '...' articles du code. Que faire si vous appelez quelque chose au cours de la première '...' qui a des effets secondaires (quelque part dans une autre partie du code), de la compensation d'une shared_ptr
de ce même objet? Et que si elle arrive à être le seul restant distinct shared_ptr
de cet objet? Bye Bye objet, juste à l'endroit où vous êtes sur le point d'essayer de l'utiliser.
Il y a donc deux façons de répondre à cette question:
Examiner la source de l'ensemble de votre programme très soigneusement jusqu'à ce que vous êtes sûr que l'objet ne meurent pas pendant le corps de la fonction.
Modifier le paramètre de retour à un objet distincts au lieu d'une référence.
Général peu de conseil qui s'applique ici: ne vous souciez pas faire risqué de changer votre code pour des raisons de performance jusqu'à ce que vous avez chronométré votre produit dans une situation réaliste dans un profiler et mesurée de façon concluante que le changement que vous voulez faire va faire une différence significative de rendement.
Mise à jour pour l'intervenant JQ
Voici un exemple artificiel. C'est délibérément simple, de sorte que l'erreur sera évident. Dans des exemples concrets, l'erreur n'est pas si évident, parce qu'il est caché dans les couches de détail réel.
Nous avons une fonction qui va envoyer un message quelque part. Il peut être un grand message d'une façon plutôt que d'utiliser un std::string
que probablement copié comme il est passé autour de plusieurs endroits, nous utilisons un shared_ptr
chaîne:
void send_message(std::shared_ptr<std::string> msg)
{
std::cout << (*msg.get()) << std::endl;
}
(Nous venons de l'envoyer à la console pour cet exemple).
Maintenant, nous voulons ajouter un établissement à retenir le message précédent. Nous voulons que le comportement suivant: une variable doit exister qui contient le plus récemment envoyé un message, mais alors qu'un message est actuellement envoyé alors il doit y avoir aucun message précédent (la variable doit être réinitialisé avant de l'envoyer). Si nous déclarons la variable:
std::shared_ptr<std::string> previous_message;
Puis nous avons modifier notre fonction selon les règles que nous avons spécifié:
void send_message(std::shared_ptr<std::string> msg)
{
previous_message = 0;
std::cout << *msg << std::endl;
previous_message = msg;
}
Donc, avant de commencer l'envoi de nous jeter l'actuel message précédent, et puis après l'envoi est terminé, nous pouvons stocker le nouveau message précédent. Du tout bon. Voici un code de test:
send_message(std::shared_ptr<std::string>(new std::string("Hi")));
send_message(previous_message);
Et comme prévu, cela s'imprime Hi!
deux fois.
Maintenant vient le long M. de Responsable, qui regarde le code et pense: Hé, que le paramètre send_message
est shared_ptr
:
void send_message(std::shared_ptr<std::string> msg)
Évidemment, cela peut être changé:
void send_message(const std::shared_ptr<std::string> &msg)
Pensez à l'amélioration de la performance de cette apportera! (Ne jamais oublier que nous sommes sur le point d'envoyer un message de grande taille sur certains canaux, de sorte que l'amélioration de la performance sera si faible qu'il peut être unmeasureable).
Mais le vrai problème est que maintenant le code de test présentent un comportement indéterminé (dans Visual C++ 2010 debug, il se bloque).
Monsieur le Responsable est surpris par cela, mais il ajoute une défensive vérifiez send_message
dans une tentative pour éviter que le problème se produit:
void send_message(const std::shared_ptr<std::string> &msg)
{
if (msg == 0)
return;
Mais bien sûr, il va toujours de l'avant et se bloque, parce qu' msg
n'est jamais null lorsque l' send_message
est appelé.
Comme je l'ai dit, avec tout le code de tellement proches dans un exemple trivial, il est facile de trouver l'erreur. Mais dans la vraie programmes, avec des relations plus complexes entre les objets mutables qui détiennent des pointeurs vers les uns des autres, il est facile de faire l'erreur, et difficile à construire le nécessaire en cas de test pour détecter l'erreur.
La solution de facilité, où vous voulez une fonction de pouvoir compter sur un shared_ptr
continue à être non-nulle dans l'ensemble, est pour la fonction à attribuer ses propres true shared_ptr
, plutôt que de s'appuyer sur une référence à un shared_ptr
.
L'inconvénient est qu'un copié shared_ptr
n'est pas libre: même "lock-free" implémentations utiliser un contrefil opération à l'honneur le filetage de garanties. Il peut donc y avoir des situations où un programme peut être considérablement accéléré par la modification d'un shared_ptr
en shared_ptr &
. Mais ce n'est pas un changement qui peut être fait en toute sécurité à tous les programmes. Il modifie le sens logique du programme.
Notez qu'un bug similaire pourrait se produire si nous avons utilisé std::string
tout au long de la place de std::shared_ptr<std::string>
, et, au lieu de:
previous_message = 0;
pour effacer le message, nous dit:
previous_message.clear();
Alors le symptôme serait la date de l'envoi d'un message vide, au lieu d'un comportement indéterminé. Le coût d'une copie supplémentaire d'une très grande chaîne peut être beaucoup plus important que le coût de la copie d'un shared_ptr
, de sorte que le compromis peut être différent.