Oui, on est sur la pile, l'autre sur le tas. Il y a deux différences importantes:
- La première, la plus évidente, et la moins importante: les allocations de Tas sont lents. Pile allocations sont rapides.
- Deuxième, et beaucoup plus important, c'est RAII. Parce que la pile alloué version est automatiquement nettoyé, il est utile. Le destructeur est appelé automatiquement, ce qui permet de garantir que les ressources allouées par la classe en débarrasser. C'est essentiellement la façon dont vous éviter les fuites de mémoire en C++. Vous les éviter en ne l'appelant
delete
vous-même, mais en l'enveloppant dans de la pile des objets alloué qui appellent delete
en interne, typiquement dans leur destructeur. Si vous tentez d'manuellement garder une trace de toutes les attributions, et appelez - delete
, au bon moment, je vous garantie que vous aurez au moins une fuite de mémoire pour 100 lignes de code.
Un petit exemple, considérer ce code:
class Pixel {
public:
Pixel(){ x=0; y=0;};
int x;
int y;
};
void foo() {
Pixel* p = new Pixel();
p->x = 2;
p->y = 5;
bar();
delete p;
}
Assez innocent code, non? Nous avons créer un pixel, puis nous appeler sans rapport avec la fonction, puis nous supprimer le pixel. Est-il une fuite de mémoire?
Et la réponse est "peut-être". Ce qui se passe si bar
déclenche une exception? delete
n'est jamais appelée, le pixel n'est jamais supprimé, et nous avons une fuite de mémoire. Maintenant, considérez ceci:
void foo() {
Pixel p;
p.x = 2;
p.y = 5;
bar();
}
Ce ne sera pas une fuite de mémoire. Bien sûr, dans ce cas simple, tout est sur la pile, donc il s'est nettoyé automatiquement, mais même si l' Pixel
classe avait fait une allocation dynamique interne, qui ne serait pas de fuite non plus. L' Pixel
classe serait tout simplement donné un destructeur que le supprime, et ce destructeur sera appelé n'importe comment, nous laisser l' foo
fonction. Même si on le laisse parce qu' bar
a déclenché une exception. Le suivant, un peu artificiel exemple montre ceci:
class Pixel {
public:
Pixel(){ x=new int(0); y=new int(0);};
int* x;
int* y;
~Pixel() {
delete x;
delete y;
}
};
void foo() {
Pixel p;
*p.x = 2;
*p.y = 5;
bar();
}
Le Pixel classe maintenant en interne alloue la mémoire du tas, mais son destructeur prend soin de la nettoyer, de sorte que lors de l' utilisation de la classe, nous n'avons pas à s'en soucier. (Je devrais probablement mentionner que le dernier exemple est simplifié beaucoup de choses, afin de montrer le principe général. Si nous étions réellement utiliser cette classe, il contient plusieurs erreurs possibles. Si la répartition de y échoue, x n'est jamais libéré, et si le Pixel est copié, nous nous retrouvons avec les deux cas, essayez de supprimer les mêmes données. Afin de prendre le dernier exemple ici avec un grain de sel. Dans le monde réel de code est un peu plus compliqué, mais il montre que l'idée générale)
Bien sûr, la même technique peut être étendue à d'autres ressources que les allocations de mémoire. Par exemple, il peut être utilisé pour garantir que les fichiers ou les connexions de base de données sont fermés après utilisation, ou de verrous de synchronisation pour votre enfilage code sont libérés.