Considérez l'extrait de code suivant:
#include <vector>
using namespace std;
void sub(vector<int>& vec) {
vec.Push_back(5);
}
int main() {
vector<int> vec(4,0);
sub(vec);
return 0;
}
En supposant que "vec" n'a plus d'espace pour stocker le 5 dans la fonction "sub", où alloue-t-il de la nouvelle mémoire?
Dans le cadre de pile de la sous-fonction? Dans ce cas, le 5 serait supprimé à la fin de la sous-fonction. Mais le cadre de pile de la fonction principale ne peut pas grandir, car le cadre de pile de la fonction secondaire se trouve au-dessus de la pile à ce moment.
Un vecteur std :: alloue-t-il de la mémoire pour ses éléments sur le tas? Mais comment libère-t-il cette mémoire de tas? S'il s'agit d'un vecteur local sur la pile, le cadre de pile d'une fonction incluant le vecteur est supprimé à la fin sans signaler au vecteur qu'il sera supprimé?
Un vecteur std :: alloue-t-il de la mémoire pour ses éléments sur le tas?
Oui. Ou plus précisément, il attribue en fonction de l'allocateur que vous passez à la construction. Vous n'en avez pas spécifié un, vous obtenez donc l'allocateur par défaut. Par défaut, ce sera le tas .
Mais comment libère-t-il cette mémoire de tas?
Par son destructeur quand il sort du domaine. (Notez qu'un pointeur vers un vecteur hors de portée ne déclenchera pas le destructeur). Mais si vous aviez passé par valeur à sub
vous construiriez (et plus tard, détruisez) une nouvelle copie. 5 serait alors repoussé sur cette copie, la copie serait nettoyée et le vecteur dans main
serait intact.
Tous les conteneurs de la STL sont paramétrés avec des arguments de modèle, généralement le dernier argument est appelé A
ou Allocator
et par défaut à std::allocator<...>
où ...
représente le type de la valeur stockée dans le conteneur.
Allocator
est une classe qui est utilisée pour fournir de la mémoire et construire/détruire les éléments dans cette zone de mémoire. Il peut allouer de la mémoire à partir d'un pool ou directement à partir du tas, selon la construction de l'allocateur. Par défaut, le std::allocator<T>
est un simple wrapper autour de ::operator new
et allouera ainsi de la mémoire sur le tas comme vous l'avez déduit.
La mémoire est allouée à la demande et est au moins désallouée lorsque le destructeur de vector
est appelé. C++ 11 introduit shrink_to_fit
pour libérer de la mémoire plus tôt aussi. Enfin, lorsque le vecteur dépasse sa capacité actuelle, une nouvelle allocation (plus importante) est effectuée, les objets y sont déplacés et l'ancienne allocation est libérée.
Comme toutes les variables locales, le destructeur est appelé lorsque exécuté atteint la fin de la portée dans laquelle il a été déclaré. Ainsi, avant de quitter la fonction, le destructeur de vecteur est appelé, et ce n'est qu'après que la pile se rétrécit et que le contrôle revient à l'appelant.
Notez également que votre vecteur (vec
) est l'objet lui-même. Il réside sur la pile et lorsque cet objet sort de la portée (ce qui est la fin de main
dans votre cas), il est détruit. La mémoire des éléments est allouée lors de l'initialisation de cet objet et libérée avec sa destruction, ce qui est un bel exemple de l'idiome RAII , car la gestion des ressources des éléments est liée à la durée de vie de l'objet vectoriel.
Parce que vous avez donné à sub l'adresse du vecteur dans le tas, elle sera allouée dans le tas. S'il n'y a plus d'espace, une exception doit être levée.