Renvoie les pointeurs intelligents par valeur.
Comme vous l'avez dit, si vous le renvoyez par référence, vous n'incrémenterez pas correctement le nombre de références, ce qui risque de supprimer quelque chose au mauvais moment. Cela seul devrait être une raison suffisante pour ne pas revenir par référence. Les interfaces doivent être robustes.
Le problème de coût est de nos jours sans objet grâce à l' optimisation de la valeur de retour (RVO), vous n'encourrez donc pas une séquence d'incrémentation-incrémentation-décrémentation ou quelque chose du genre dans les compilateurs modernes. Donc, la meilleure façon de renvoyer a shared_ptr
est simplement de retourner par valeur:
shared_ptr<T> Foo()
{
return shared_ptr<T>(/* acquire something */);
};
C'est une opportunité RVO évidente pour les compilateurs C ++ modernes. Je sais pertinemment que les compilateurs Visual C ++ implémentent RVO même lorsque toutes les optimisations sont désactivées. Et avec la sémantique de déplacement de C ++ 11, cette préoccupation est encore moins pertinente. (Mais le seul moyen d'être sûr est de profiler et d'expérimenter.)
Si vous n'êtes toujours pas convaincu, Dave Abrahams a un article qui propose un retour par valeur. Je reproduis un extrait ici; Je vous recommande vivement d'aller lire l'article en entier:
Soyez honnête: que ressentez-vous avec le code suivant?
std::vector<std::string> get_names();
...
std::vector<std::string> const names = get_names();
Franchement, même si je devrais savoir mieux, cela me rend nerveux. En principe, lors des get_names()
retours, nous devons copier a vector
de l' string
art. Ensuite, nous devons le copier à nouveau lors de l'initialisation
names
, et nous devons détruire la première copie. S'il y a N string
s dans le vecteur, chaque copie peut nécessiter jusqu'à N + 1 allocations de mémoire et toute une série d'accès aux données non compatibles avec le cache> lorsque le contenu de la chaîne est copié.
Plutôt que d'affronter ce genre d'anxiété, je me suis souvent rabattu sur le passage par référence pour éviter les copies inutiles:
get_names(std::vector<std::string>& out_param );
...
std::vector<std::string> names;
get_names( names );
Malheureusement, cette approche est loin d'être idéale.
- Le code a augmenté de 150%
- Nous avons dû abandonner
const
-ness parce que nous mutons des noms.
- Comme les programmeurs fonctionnels aiment nous le rappeler, la mutation rend le code plus complexe à raisonner en sapant la transparence référentielle et le raisonnement équationnel.
- Nous n'avons plus de sémantique de valeur stricte pour les noms.
Mais est-il vraiment nécessaire de gâcher notre code de cette manière pour gagner en efficacité? Heureusement, la réponse s'avère non (et surtout pas si vous utilisez C ++ 0x).