Le code suivant (func1 ()) est-il correct s'il doit renvoyer i? Je me souviens avoir lu quelque part qu'il y avait un problème lors du renvoi d'une référence à une variable locale. En quoi est-ce différent de func2 ()?
int& func1()
{
int i;
i = 1;
return i;
}
int* func2()
{
int* p;
p = new int;
*p = 1;
return p;
}
int& i = * new int;
Réponses:
Cet extrait de code:
ne fonctionnera pas car vous renvoyez un alias (une référence) à un objet avec une durée de vie limitée à la portée de l'appel de fonction. Cela signifie qu'une fois
func1()
retourne,int i
meurt, ce qui rend la référence retournée par la fonction sans valeur car elle fait maintenant référence à un objet qui n'existe pas.La deuxième version fonctionne car la variable est allouée sur le magasin gratuit, qui n'est pas lié à la durée de vie de l'appel de fonction. Cependant, vous êtes responsable de la
delete
somme allouéeint
.En règle générale, vous encapsulez le pointeur dans une classe RAII et / ou une fonction d'usine afin que vous n'ayez pas à le
delete
faire vous-même.Dans les deux cas, vous pouvez simplement renvoyer la valeur elle-même (bien que je réalise que l'exemple que vous avez fourni était probablement artificiel):
Notez qu'il est parfaitement bien de renvoyer de gros objets de la même manière que
func3()
les valeurs primitives, car à peu près tous les compilateurs implémentent de nos jours une forme d' optimisation de la valeur de retour :Fait intéressant, lier un temporaire à une référence const est parfaitement légal en C ++ .
la source
int* p = func2(); delete p;
Maintenant, lorsque vous avez supprimé 'p', cela signifie-t-il que la mémoire allouée "à l'intérieur" de lafunc2()
définition de la fonction a également été supprimée?func2()
et libérée à l'extérieur dans la ligne suivante. C'est une façon plutôt sujette aux erreurs de gérer la mémoire, comme je l'ai dit, vous utiliseriez plutôt une variante de RAII. Au fait, vous semblez apprendre le C ++. Je recommande de prendre un bon livre d'introduction au C ++ pour apprendre. De plus, pour référence future si vous avez une question, vous pouvez toujours la publier sur Stack Overflow. Les commentaires ne sont pas destinés à poser des questions totalement nouvelles.Une variable locale est la mémoire sur la pile, cette mémoire n'est pas automatiquement invalidée lorsque vous sortez de la portée. À partir d'une fonction imbriquée plus profondément (plus haut sur la pile en mémoire), il est parfaitement sûr d'accéder à cette mémoire.
Une fois que la fonction revient et se termine, les choses deviennent dangereuses. Habituellement, la mémoire n'est pas supprimée ou écrasée lorsque vous revenez, ce qui signifie que la mémoire à ces adresses contient toujours vos données - le pointeur semble valide.
Jusqu'à ce qu'une autre fonction crée la pile et la remplace. C'est pourquoi cela peut fonctionner pendant un certain temps - puis cesser soudainement de fonctionner après qu'un ensemble de fonctions particulièrement imbriquées, ou une fonction avec une taille vraiment énorme ou de nombreux objets locaux, atteigne à nouveau cette pile-mémoire.
Il peut même arriver que vous atteigniez à nouveau la même partie de programme et que vous écrasiez votre ancienne variable de fonction locale par la nouvelle variable de fonction. Tout cela est très dangereux et doit être fortement découragé. N'utilisez pas de pointeurs vers des objets locaux!
la source
Une bonne chose à retenir sont ces règles simples, et elles s'appliquent à la fois aux paramètres et aux types de retour ...
Il y a un moment et un lieu pour chacun, alors assurez-vous de les connaître. Les variables locales, comme vous l'avez montré ici, ne sont que cela, limitées au temps où elles sont localement actives dans la portée de la fonction. Dans votre exemple, avoir un type de
int*
retour et un retour&i
aurait été tout aussi incorrect. Vous feriez mieux dans ce cas de faire cela ...Cela changerait directement la valeur de votre paramètre passé. Alors que ce code ...
pas. Cela changerait simplement la valeur de
oValue
local à l'appel de fonction. La raison en est que vous ne changeriez en fait qu'une copie "locale" deoValue
, et nonoValue
lui-même.la source