Il y a deux largement utilisés des techniques d'allocation de mémoire: automatique allocation et l'allocation dynamique. Généralement, il est une région correspondante de la mémoire pour chacun: la pile et le tas.
Pile
La pile toujours alloue de la mémoire, de façon séquentielle. Il peut le faire parce qu'il vous oblige à libérer de la mémoire dans l'ordre inverse (Premier entré, Dernier Sorti: FILO). C'est l'allocation de mémoire technique pour les variables locales dans de nombreux langages de programmation. Il est très, très rapide, car il nécessite un minimum de tenue de livres et la prochaine adresse à allouer est implicite.
En C++, cela s'appelle du stockage automatique , car le stockage est revendiquée automatiquement à la fin de la portée. Dès que l'exécution de l'actuel bloc de code (délimité à l'aide de {}
) est terminée, la mémoire pour toutes les variables de ce bloc est automatiquement collectées. C'est aussi le moment où les destructeurs sont appelés à nettoyer les ressources.
Tas
Le tas permet une plus souple de l'allocation de mémoire de mode. La comptabilité est plus complexe et la répartition est plus lent. Car il n'est pas implicite point de lâcher, vous devez libérer la mémoire manuellement, à l'aide de delete
ou delete[]
(free
en C). Cependant, l'absence d'un accord implicite de la libération point est la clé pour le segment de la flexibilité.
Raisons de l'utilisation de l'allocation dynamique
Même si le tas est plus lente et peut entraîner des fuites de mémoire ou de fragmentation de mémoire, il y a parfaitement bien des cas d'utilisation pour l'allocation dynamique, car elle est moins limitée.
Deux raisons principales pour l'utilisation de l'allocation dynamique:
Vous ne savez pas combien de mémoire vous avez besoin au moment de la compilation. Par exemple, lors de la lecture d'un fichier texte dans une chaîne de caractères, généralement, vous ne savez pas quelle est la taille du fichier, de sorte que vous ne pouvez pas décider de la quantité de mémoire à allouer jusqu'à ce que vous exécutez le programme.
Vous souhaitez allouer de la mémoire qui vont persister après la sortie du bloc courant. Par exemple, vous pouvez écrire une fonction string readfile(string path)
qui renvoie le contenu d'un fichier. Dans ce cas, même si la pile pourrait tenir l'ensemble du contenu du fichier, vous ne pouviez pas de retour de la fonction et de garder le bloc de mémoire allouée.
Pourquoi l'allocation dynamique est souvent inutile
En C++ il y a une jolie construction appelée un destructeur. Ce mécanisme permet de gérer les ressources en alignant la durée de vie de la ressource avec la durée de vie d'une variable. Il est couramment utilisé pour "envelopper" les ressources dans un autre objet. std::string
est un parfait exemple. Cet extrait:
int main ( int argc, char* argv[] )
{
std::string program(argv[0]);
}
en fait alloue un montant variable de la mémoire. L' std::string
objet alloue de la mémoire à l'aide de la tas et la libère dans son destructeur. Dans ce cas, vous n'avez pas besoin de gérer manuellement toutes les ressources et encore obtenir les avantages de l'allocation dynamique de la mémoire.
En particulier, il implique que, dans cet extrait:
int main ( int argc, char* argv[] )
{
std::string * program = new std::string(argv[0]);
delete program;
}
il est inutile de l'allocation dynamique de la mémoire. Le programme nécessite plus de frappe (!) et introduit le risque d'oublier de libérer la mémoire. Il le fait avec aucun avantage apparent.
Pourquoi devriez-vous utiliser le stockage automatique aussi souvent que possible
Fondamentalement, le dernier paragraphe résume. À l'aide de stockage automatique aussi souvent que possible vos programmes:
- plus rapide de taper;
- plus rapide lors de l'exécution;
- moins sujettes à la mémoire de ressources de fuites.
Les points de Bonus
Référencé dans la question, il y a d'autres préoccupations. En particulier, la classe suivante:
class Line {
public:
Line();
~Line();
std::string* mString;
};
Line::Line() {
mString = new std::string("foo_bar");
}
Line::~Line() {
//mString->clear(); // should not be neccessary
delete mString;
}
Est en réalité beaucoup plus risqué d'utiliser que la suivante:
class Line {
public:
Line();
std::string mString;
};
Line::Line() {
mString = "foo_bar";
// note: there is a cleaner way to write this.
}
La raison en est qu' std::string
correctement définit un constructeur de copie. Considérons le programme suivant:
int main ()
{
Line l1;
Line l2 = l1;
}
À l'aide de la version d'origine, ce programme sera probablement crash, comme il utilise delete
sur la même chaîne à deux reprises. À l'aide de la version modifiée, chaque Line
instance propre sa propre chaîne de l'instance, chacune avec sa propre mémoire, et les deux seront publiées à la fin du programme.
D'autres notes
L'utilisation intensive de RAII est considéré comme une meilleure pratique en C++ à cause de toutes les raisons ci-dessus. Cependant, il y a un avantage supplémentaire qui n'est pas immédiatement évidente. En gros, c'est mieux que la somme de ses parties. L'ensemble du mécanisme qui le compose. Il évolue.
Si vous utilisez l' Line
classe comme un bloc de construction:
class Table
{
Line borders[4];
};
Alors
int main ()
{
Table table;
}
alloue quatre std::string
des cas, quatre Line
des cas, Table
de l'instance et de toute la chaîne du matières et tout est libérée automatiquement.