126 votes

Bon usage pile et tas en C++ ?

J'ai été à la programmation pour un moment, mais Elle a été principalement Java et C#. Je n'ai jamais réellement eu à gérer la mémoire sur mon propre. J'ai récemment commencé la programmation en C++ et je suis un peu confus quand je dois stocker des choses sur la pile et quand les stocker sur le tas.

Ma compréhension est que les variables qui sont d'un accès très fréquemment doivent être stockées sur la pile et les objets, rarement utilisé des variables, et les grandes structures de données devraient tous être stockés sur le tas. Est-ce correct ou je suis incorrect?

245voto

Crashworks Points 22920

Non, la différence entre la pile et le tas n'est pas la performance. C'est la durée de vie: toute variable locale dans une fonction (ce que vous n'avez pas de malloc() ou la nouvelle) vie de la pile. Il disparaît lorsque vous êtes de retour de la fonction. Si vous voulez quelque chose à vivre plus longtemps que la fonction qui l'a déclaré, vous devez allouer sur le tas.

class Thingy;

Thingy* foo( ) 
{
  int a; // this int lives on the stack
  Thingy B; // this thingy lives on the stack and will be deleted when we return from foo
  Thingy *pointerToB = &B; // this points to an address on the stack
  Thingy *pointerToC = new Thingy(); // this makes a Thingy on the heap.
                                     // pointerToC contains its address.

  // this is safe: C lives on the heap and outlives foo().
  // Whoever you pass this to must remember to delete it!
  return pointerToC;

  // this is NOT SAFE: B lives on the stack and will be deleted when foo() returns. 
  // whoever uses this returned pointer will probably cause a crash!
  return pointerToB;
}

Pour une meilleure compréhension de ce que la pile est, venir à partir de l'autre extrémité, plutôt que d'essayer de comprendre ce que la pile ne dans les termes d'un langage de haut niveau, une recherche de "call stack" et "convention d'appel" et de voir ce que la machine n'a vraiment lorsque vous appelez une fonction. La mémoire de l'ordinateur est juste une série d'adresses; "tas" et la "pile" sont des inventions du compilateur.

43voto

MarkR Points 37178

Je dirais:

La stocker dans la pile, si vous le POUVEZ.

Les stocker sur le tas, si vous en avez BESOIN.

Par conséquent, préfèrent la pile sur le tas. Quelques raisons qui vous ne pouvez pas garder quelque chose sur la pile sont:

  • C'est trop gros sur les programmes multithread sur 32-bit OS, la pile a une petite et fixe (à la création de thread temps au moins) taille (typiquement de quelques mégas. Ceci est fait de sorte que vous pouvez créer beaucoup de fils, sans l'épuiser l'espace d'adresse. Pour les programmes 64 bits, ou mono-thread (sous Linux en tout cas) des programmes, ce n'est pas un problème majeur. Sous Linux 32 bits, mono-thread programmes utilisent généralement dynamique des piles qui peuvent continuer de croître jusqu'à ce qu'ils atteignent le haut du tas.
  • Vous avez besoin d'y accéder en dehors de la portée de l'original frame de pile - c'est vraiment la raison principale.

Il est possible, avec des compilateurs, pour allouer de la non-correction de la taille des objets sur le tas (généralement des tableaux dont la taille n'est pas connue au moment de la compilation).

24voto

Daniel Earwicker Points 63298

C'est plus subtil que les autres réponses suggèrent. Il n'est pas absolue fossé entre les données sur la pile et les données sur le tas en fonction de comment vous le déclarer. Par exemple:

std::vector<int> v(10);

Dans le corps d'une fonction, qui déclare vector (tableau dynamique) de dix entiers sur la pile. Mais l'espace de stockage gérés par l' vector n'est pas sur la pile.

Ah, mais (les autres réponses suggèrent) de la durée de stockage est limitée par la durée de vie de l' vector lui-même, qui, ici, est basée sur la pile, de sorte qu'il ne fait aucune différence comment il est mis en œuvre - nous ne pouvons la traiter comme une pile d'objets basé sur la valeur sémantique.

De ne pas faire. Supposons que la fonction est:

void GetSomeNumbers(std::vector<int> &result)
{
    std::vector<int> v(10);

    // fill v with numbers

    result.swap(v);
}

Si quoi que ce soit avec un swap de la fonction (et de tout complexe de type de valeur devrait en avoir un) peut servir comme une sorte de rebindable référence à un tas de données, dans un système qui garantit un seul propriétaire des données.

Donc le C++ moderne approche est de ne jamais stocker l'adresse de segment de données dans nu pointeur variables. Toutes les allocations de tas doivent être cachés à l'intérieur des classes.

Si vous faites cela, vous pouvez penser à toutes les variables de votre programme comme s'ils étaient de simples types de valeur, et oublier le tas tout (sauf lors de l'écriture d'une nouvelle valeur de type classe wrapper pour certains tas de données, ce qui devrait être rare).

Il vous suffit simplement de retenir les services d'un bit spécial de connaissances pour vous aider à optimiser: si possible, au lieu d'affecter une variable à une autre, comme ceci:

a = b;

swap comme ceci:

a.swap(b);

parce que c'est beaucoup plus rapide et il ne jette pas des exceptions. La seule exigence est que vous n'avez pas besoin d' b de continuer à garder la même valeur (c'est pour obtenir apar rapport au lieu, qui serait mis à la corbeille en a = b).

L'inconvénient est que cette approche vous oblige à les valeurs de retour des fonctions via les paramètres de sortie au lieu de la valeur de retour. Mais ils sont de fixation que dans C++0x avec des références rvalue.

Dans les situations les plus complexes de tous, vous devez prendre cette idée au général extrême et l'utilisation d'un pointeur intelligent de la classe comme l' shared_ptr ce qui est déjà dans tr1. (Bien que je dirais que si vous avez l'impression d'en avoir besoin, vous avez probablement déplacé à l'extérieur de la Norme C++'sweet spot de l'applicabilité.)

6voto

1800 INFORMATION Points 55907

Vous avez également de stocker un élément sur le tas si elle doit être utilisée en dehors de la portée de la fonction dans laquelle il est créé. Un idiome utilisé avec pile d'objets est appelé RAII - cela implique l'utilisation de la pile en fonction de l'objet de l'enveloppe pour une ressource, lorsque l'objet est détruit, la ressource pourrait être nettoyé. Pile de base des objets sont plus faciles à garder une trace de quand vous pourriez être jeter exceptions - vous n'avez pas besoin de vous soucier de la suppression d'un tas d'objets dans un gestionnaire d'exception. C'est pourquoi, la puissance de pointeurs ne sont pas normalement utilisées en C++ moderne, vous pouvez utiliser un pointeur intelligent qui peut être une pile en fonction de l'enveloppe pour un pointeur brut à un segment en fonction de l'objet.

5voto

Nick Points 5293

Pour ajouter d'autres réponses, il peut aussi être sujet de la performance, au moins un peu. Non pas que vous devriez vous inquiéter à ce sujet, sauf s'il est pertinent pour vous, mais:

L'allocation dans le tas, il doit y avoir un suivi d'un bloc de mémoire, ce qui n'est pas une constante de temps de l'opération (et prend un peu de cycles et les frais généraux). Cela peut être un plus lent que la mémoire est fragmentée, et/ou que vous obtenez près de 100% de votre espace d'adressage. D'autre part, la pile des allocations de constante de temps, en gros "libre".

Une autre chose à considérer (encore une fois, vraiment important que si elle devient un problème) est que, généralement, la taille de la pile est fixe, et peut être bien inférieur à la taille du tas. Donc, si vous êtes l'allocation de grands objets ou de petits objets, vous voudrez probablement utiliser le segment de mémoire; si vous manquez d'espace de pile, l'exécution lèvera le site titulaire d'exception. Généralement pas une grosse affaire, mais une autre chose à considérer.

Espérons que cette aide.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X