Quand est-il préférable d’utiliser la stack au lieu du tas et vice versa?

En C ++, quand est-il préférable d’utiliser la stack? Quand est-il préférable d’utiliser le tas?

Utilisez la stack lorsque votre variable ne sera pas utilisée après le retour de la fonction en cours. Utilisez le tas lorsque les données de la variable sont nécessaires au-delà de la durée de la fonction en cours.

En règle générale, évitez de créer des objects énormes sur la stack.

  • La création d’un object sur la stack vous libère de la nécessité de vous souvenir de nettoyer (lire, supprimer) l’object. Mais créer trop d’objects sur la stack augmente les risques de débordement de stack.
  • Si vous utilisez heap pour l’object, vous obtenez autant de mémoire que le système d’exploitation peut en fournir, beaucoup plus grande que la stack, mais encore une fois, vous devez vous assurer de libérer la mémoire lorsque vous avez terminé. De même, créer trop d’objects dans le tas aura tendance à fragmenter la mémoire, ce qui affectera à son tour les performances de votre application.

Utilisez la stack lorsque la mémoire utilisée est ssortingctement limitée à la scope dans laquelle vous la créez. Ceci est utile pour éviter les memory leaks car vous savez exactement où vous voulez utiliser la mémoire, et vous savez quand vous n’en avez plus besoin, de sorte que la mémoire sera nettoyée pour vous.

int main() { if (...) { int i = 0; } // I know that i is no longer needed here, so declaring i in the above block // limits the scope appropriately } 

Le tas, cependant, est utile lorsque vous pouvez accéder à votre mémoire en dehors du cadre de sa création et que vous ne souhaitez pas copier une variable de stack. Cela peut vous donner un contrôle explicite sur la manière dont la mémoire est allouée et libérée.

 Object* CreateObject(); int main() { Object* obj = CreateObject(); // I can continue to manipulate object and I decide when I'm done with it // .. // I'm done delete obj; // .. keep going if you wish return 0; } Object* CreateObject() { Object* returnValue = new Object(); // ... do a bunch of stuff to returnValue return returnValue; // Note the object created via new here doesn't go away, its passed back using // a pointer } 

Évidemment, un problème courant est que vous pouvez oublier de supprimer votre object. Cela s’appelle une fuite de mémoire. Ces problèmes sont plus fréquents car votre programme devient de moins en moins sortingvial lorsque la «propriété» (ou qui est exactement responsable de la suppression des éléments) devient plus difficile à définir.

Les solutions courantes dans les langages plus gérés (C #, Java) consistent à implémenter la récupération de place afin de ne pas avoir à penser à la suppression d’éléments. Cependant, cela signifie qu’il y a quelque chose en arrière-plan qui s’exécute de manière apériodique pour vérifier vos données de tas. Dans un programme non sortingvial, cela peut devenir plutôt inefficace car un thread de «récupération de place» apparaît et cherche à trouver des données qui doivent être supprimées, alors que le rest de votre programme est bloqué.

En C ++, la solution la plus courante et la meilleure (à mon avis) pour gérer les memory leaks consiste à utiliser un pointeur intelligent. Le plus commun est boost :: shared_ptr qui est ( référence comptée )

Donc, pour recréer l’exemple ci-dessus boost :: shared_ptr CreateObject ();

 int main() { boost::shared_ptr obj = CreateObject(); // I can continue to manipulate object and I decide when I'm done with it // .. // I'm done, manually delete obj.reset(NULL); // .. keep going if you wish // here, if you forget to delete obj, the shared_ptr's destructor will note // that if no other shared_ptr's point to this memory // it will automatically get deleted. return 0; } boost::shared_ptr CreateObject() { boost::shared_ptr returnValue(new Object()); // ... do a bunch of stuff to returnValue return returnValue; // Note the object created via new here doesn't go away, its passed back to // the receiving shared_ptr, shared_ptr knows that another reference exists // to this memory, so it shouldn't delete the memory } 

Une exception à la règle mentionnée ci-dessus selon laquelle vous devez généralement utiliser la stack pour les variables locales qui ne sont pas nécessaires en dehors de la scope de la fonction:

Les fonctions récursives peuvent épuiser l’espace de la stack si elles allouent des variables locales volumineuses ou si elles sont invoquées récursivement plusieurs fois. Si vous avez une fonction récursive qui utilise la mémoire, il peut être judicieux d’utiliser de la mémoire basée sur des segments au lieu de la mémoire basée sur la stack.

Cette question est liée (mais pas vraiment une dupe) à Quoi et où sont la stack et le tas , qui a été demandé il y a quelques jours.

En règle générale, utilisez la stack quand vous le pouvez. c’est-à-dire lorsque la variable n’est jamais nécessaire en dehors de cette scope.

c’est plus rapide, provoque moins de fragmentation et va éviter les autres surcharges associées à l’appel de malloc ou new. l’allocation hors de la stack est un couple d’opérations d’assembleur, malloc ou new est plusieurs centaines de lignes de code dans une implémentation efficace.

il n’est jamais préférable d’utiliser le tas … juste inévitable. 🙂

Utilisez le tas uniquement pour allouer de l’espace aux objects lors de l’exécution. Si vous connaissez la taille au moment de la compilation, utilisez la stack. Au lieu de renvoyer des objects alloués au tas à partir d’une fonction, passez un tampon dans la fonction pour qu’il écrive. De cette manière, le tampon peut être alloué là où la fonction est appelée en tant que tableau ou autre structure basée sur la stack.

Moins vous avez d’instructions malloc (), moins il y a de risques de memory leaks.

La question est mal formée.

Il y a des situations où vous avez besoin de la stack, d’autres où vous avez besoin du tas, d’autres où vous avez besoin du stockage statique, d’autres où vous avez besoin des données de mémoire const, d’autres où vous avez besoin du magasin gratuit.

La stack est rapide, car l’allocation est juste un “incrément” sur le SP, et toute “allocation” est effectuée au moment de l’invocation de la fonction dans laquelle vous vous trouvez. .