Le retour de l'objet à partir de la fonction
Je suis vraiment confus maintenant sur la manière et la méthode à utiliser pour objet de retour d'une fonction. Je veux des commentaires sur les solutions pour les exigences.
Scénario A:
L'objet retourné doit être stocké dans une variable qui n'a pas besoin d'être modifiée au cours de sa durée de vie. Ainsi,
const Foo SomeClass::GetFoo() {
return Foo();
}
invoquée comme:
someMethod() {
const Foo& l_Foo = someClassPInstance->GetFoo();
//...
}
Scneraio B:
L'objet retourné doit être stocké dans une variable qui sera modifiée au cours de sa durée de vie. Ainsi,
void SomeClass::GetFoo(Foo& a_Foo_ref) {
a_Foo_ref = Foo();
}
invoquée comme:
someMethod() {
Foo l_Foo;
someClassPInstance->GetFoo(l_Foo);
//...
}
J'ai une question ici: Permet de dire que Toto ne peut pas avoir un constructeur par défaut. Puis comment voulez-vous que dans cette situation, puisque l'on ne peux pas écrire ça:
Foo l_Foo
Scénario C:
Foo SomeClass::GetFoo() {
return Foo();
}
invoquée comme:
someMethod() {
Foo l_Foo = someClassPInstance->GetFoo();
//...
}
Je pense que ce n'est pas l'approche recommandée car elle entraînerait la construction supplémentaires temporaires.
Qu'en pensez-vous ? Aussi, ne vous recommander une meilleure façon de gérer cela à la place ?
- en.wikipedia.org/wiki/Return_value_optimization
- le scénario est bien, mais vraiment aucune amélioration par rapport à C. j'utilise C, à moins que Foo est le constructeur par défaut est "rapide", Foo copie ctor est "lent" et vous ne voulez pas compter sur le compilateur assez intelligent pour éluder les recopies inutiles auquel cas B est également acceptable.
- notez que
a_Foo_ref = Foo();
crée l'objet et réalise la copie. Il est à peu près équivalente pour le dernier scénario - Non, il n'est pas. Cette déclaration est autorisé à construire un seul objet.
- Je l'ai fait lire à ce sujet dans Plus Effective C++(page 104). Le scénario C est également recommandé de là. Il est suggéré que le compilateur d'optimiser le scénario C et de la "construction de l'objet défini par le retour de l'expression à l'intérieur de la mémoire allouée pour l_Foo", réduisant ainsi le coût total de temporaires à 0. Je ne sais pas comment le compilateur pourrait le faire, mais je prends de l'auteur de la parole pour l'instant. Ma question est alors, quand nous avons de grands objets, pourquoi est-Scénario C puis pas recommandé ? On m'a toujours dit qu'il implique la création/suppression d'temporaires et donc recommandé d'utiliser B.
- Toutes les recommandations pour l'utilisation de B sont probablement des gens qui ont été en utilisant les compilateurs sans RVO/NRVO. Il fut un temps où ce n'était pas une commune de l'optimisation. Cependant, presque à chaque compilateur fait dans la dernière décennie met en œuvre RVO/NRVO, de sorte que vous devriez être bien.
- Oui, il continue de s'appliquer dans ce cas. La façon dont RVO fonctionne, c'est que le code d'appel qui alloue de la mémoire pour l'objet, puis passe l'adresse de la mémoire à la fonction, qui construit alors l'objet retourné dans cette mémoire. L'appel de la
add()
fonction dans votre exemple ne serait pas interférer avec qui. Une chose qui peut causer la RVO à ne pas fonctionner, cependant, c'est lorsque la fonction crée plusieurs objets et peut renvoyer l'un d'entre eux, comme illustré ici: en.wikipedia.org/wiki/...
Vous devez vous connecter pour publier un commentaire.
Tout d'abord, regardons les choses qui entrent en jeu ici:
(a) l'Extension de la durée de vie d'un temporaire quand il est utilisé pour initialiser une référence - j'ai appris à ce sujet dans cette publication par Andrei Anexandrescu. De nouveau, il se sent bizarre, mais utile:
La règle dit que lorsqu'une référence est initialisé avec un temporaire de, temporaire de la durée de vie est prolongée jusqu'à ce que la référence est hors de portée. (cette réponse cite le canon)
(b) Valeur de Retour d'Optimisation - (wikipédia) - les deux copies locales --> valeur de retour --> local peut être omis dans les circonstances. C'est un surprenant règle, car il permet au compilateur de modifier les comportements observables, mais utile.
Là vous l'avez. C++ - bizarre, mais utile.
Donc en regardant vos scénarios
Scénario A: vous êtes de retour temporaire, et le lier à une référence - le temporaire de la durée de vie est prolongée de la durée de vie de l_Foo.
Notez que cela ne fonctionne pas si
GetFoo
serait de retour d'une référence plutôt que temporaire.Scénario B: Fonctionne, sauf qu'il forces une Construction-Construire-une Copie du Cycle (qui peut être beaucoup plus cher que de construire), et le problème que vous mentionnez au sujet de l'obligation de constructeur par défaut.
Je ne voudrais pas utiliser ce modèle pour créer un objet - seulement à muter en une.
Scénario C: Les copies temporaires peuvent être omis par le compilateur (comme de la RVO la règle). Il n'existe malheureusement pas de garanties, mais les compilateurs modernes ne mettre en œuvre RVO.
Références Rvalue dans C++ 0x permet de Foo pour mettre en œuvre une ressource chapardage constructeur qui garantit non seulement la suppression de la copie, mais s'avère utile dans d'autres scénarios.
(Je doute qu'il y a un compilateur qui implémente les références rvalue mais pas RVO. Cependant, il existe des scénarios où RVO ne peut pas kick in.)
Une question comme celle-ci nécessite de mentionner des pointeurs intelligents, tels que
shared_ptr
etunique_ptr
(le dernier étant un "coffre-fort"auto_ptr
). Ils sont également en C++ 0x. Ils fournissent un autre modèle pour les fonctions de création d'objets.Foo l_Foo; someClassPInstance->GetFoo(l_Foo);
:: Nous envoyer la référence de l_Foo, donc pas de temporaires ici. Et puis, dansvoid SomeClass::GetFoo(Foo& a_Foo_ref){ //.. }
permet simplement de dire, je le modifier et de ne pas le créer. Pensez-vous alors B est une bonne optionFoo const & f = GetFoo();
devrait lireFoo const & foo = GetFoo();
, mais la bonne réponse tout de même.Des trois scénarios, le nombre 3 est le ideomatic méthode, et celui que vous devriez utiliser. Vous n'aurez pas à payer pour les copies supplémentaires parce que le compilateur est libre d'utiliser copie élision pour éviter la copie, si possible.
Secnario Un qui est mauvais. Vous vous retrouvez avec une référence à une temporaire qui est détruit lors de la déclaration contenant l'appel de la fonction se termine.d'Accord, le Scénario est pas mal, mais vous devriez toujours utiliser le Scénario C.Secnario B fonctionne très bien, mais:
Foo doit avoir un constructeur par défaut. Même si vous ne lui donnez pas un, le compilateur doit pour vous. En supposant que vous déclarez une
private
constructeur, il n'y a aucun moyen que vous pouvez utiliser cette méthode d'appel. Vous devez soit faireFoo
par défaut constructable ou de l'utilisation Secnario C.const Foo SomeClass::GetFoo()
:: Scénario A :: je ne suis pas de retour à la référence à un temporaire ici..