Question

Je suit des questions concernant les chaînes en C ++

1 >> qui est une meilleure option (compte tenu de la performance) et pourquoi?

1.

string a;
a = "hello!";

ou

2.

string *a;
a = new string("hello!");
...
delete(a);

2 >>

string a;
a = "less"; 
a = "moreeeeeee"; 

comment exactement la gestion de la mémoire est gérée en c ++ quand une chaîne plus grande est copié dans une chaîne plus petite? Sont les chaînes de c mutable?

Était-ce utile?

La solution

Tout ce qui suit est ce qu'un compilateur naïf ferait. Bien sûr, aussi longtemps que cela ne change pas le comportement du programme, le compilateur est libre de toute optimisation.

string a;
a = "hello!";

D'abord, vous initialisez un pour contenir la chaîne vide. (Régler la longueur de 0, et un ou deux autres opérations). Ensuite, vous attribuez une nouvelle valeur, la valeur de l'écrasement longueur a déjà été fixé. Il peut également effectuer une vérification pour voir la taille de la mémoire tampon est en cours, et si oui ou non plus de mémoire doit être allouée.

string *a;
a = new string("hello!");
...
delete(a);

L'appel nouvelle nécessite le système d'exploitation et l'allocateur de mémoire pour trouver un morceau libre de mémoire. C'est lent. Ensuite, vous initialisez immédiatement, de sorte que vous n'attribuez pas quoi que ce soit deux ou besoin du tampon pour être redimensionnées, comme vous le faites dans la première version. Puis quelque chose se passe mal, et vous oubliez d'appeler supprimer, et vous avez une fuite de mémoire, en plus à une chaîne qui est extrêmement lent à allouer. Donc, cela est mauvais.

string a;
a = "less"; 
a = "moreeeeeee";

Comme dans le premier cas, vous devez d'abord initialiser un pour contenir la chaîne vide. Ensuite, vous attribuez une nouvelle chaîne, puis une autre. Chacun de ces peut exiger un appel à nouveau pour allouer plus de mémoire. Chaque ligne exige également la longueur, et éventuellement d'autres variables internes à attribuer.

Normalement, vous auriez allouer comme ceci:

string a = "hello";

Une ligne, effectuer l'initialisation une fois, plutôt que par défaut la première-initialisation, puis attribuer la valeur que vous voulez.

Il permet également de minimiser les erreurs, parce que vous ne disposez pas d'une chaîne vide de sens partout dans votre programme. Si la chaîne existe, il contient la valeur que vous voulez.

A propos de la gestion de la mémoire, google RAII. En bref, la chaîne appelle nouveau / supprimer en interne pour redimensionner son tampon. Cela signifie que vous jamais doivent allouer une chaîne nouvelle. L'objet de chaîne a une taille fixe, et est conçu pour être alloué sur la pile, de sorte que le destructeur est automatiquement appelé quand il est hors de portée. Le destructeur garantit alors que toute la mémoire allouée est libérée. De cette façon, vous n'avez pas à utiliser les nouvelles / supprimer dans votre code d'utilisateur, ce qui signifie que vous ne fuira pas la mémoire.

Autres conseils

Il est presque jamais nécessaire ou souhaitable de dire

string * s = new string("hello");

Après tout, vous (presque) jamais dire:

int * i = new int(42);

Vous devriez plutôt dire

string s( "hello" );

ou

string s = "hello";

Et oui, les chaînes C ++ sont mutables.

Y at-il une raison particulière pour laquelle vous utilisez constamment l'affectation au lieu de initialisation? C'est, pourquoi écrivez-vous pas

string a = "Hello";

etc.? Cela évite une construction par défaut et est tout simplement sémantiquement plus de sens. Création d'un pointeur sur une chaîne juste pour l'allocation sur le tas est jamais significatif, à savoir votre cas 2 n'a pas de sens et est un peu moins efficace.

Quant à votre dernière question, oui, chaînes en C ++ sont mutables à moins const déclaré.

string a;
a = "hello!";

2 opérations: appelle le constructeur par défaut std: string () et appelle ensuite l'opérateur :: =

string *a; a = new string("hello!"); ... delete(a);

une seule opération: appelle le constructeur std:. Chaîne (const char *), mais vous ne devriez pas oublier de libérer votre pointeur

Qu'en est-      String ( "bonjour");

Dans le cas 1.1, votre chaîne membres (qui comprennent pointeur sur les données) sont maintenues dans stack et la mémoire occupée par l'instance de classe est libérée lorsque a est hors de portée.

1,2 Dans le cas, de la mémoire pour les membres sont alloués dynamiquement à partir de tas aussi.

Lorsque vous attribuez une char* constante à une chaîne, la mémoire qui contiendra les données seront realloc'ed pour répondre aux nouvelles données.

Vous pouvez voir la quantité de mémoire allouée en appelant string::capacity().

Lorsque vous appelez string a("hello"), mémoire est allouée dans le constructeur.

Les deux constructeur et l'attribution appel mêmes méthodes en interne à la mémoire allouée de l'opérateur et copier les nouvelles données là-bas.

Si vous regardez le docs pour la classe de chaîne STL (I croire que la documentation SGI sont conformes à la spécification), un grand nombre de la liste des méthodes garanties de complexité. Je crois que beaucoup des garanties de complexité sont intentionnellement laissés vagues pour permettre des implémentations différentes. Je pense que certaines implémentations utilisent en fait une approche de copie en modifier de telle sorte que l'attribution d'une chaîne à l'autre est une opération constante de temps, mais vous pouvez encourir un coût inattendu lorsque vous essayez de modifier l'un de ces cas. Je ne sais pas si cela est encore vrai dans STL moderne bien.

Vous devriez également vérifier la fonction capacity(), qui vous dira la chaîne de longueur maximale que vous pouvez mettre dans une instance de chaîne donnée avant qu'il ne soit contraint de réaffecter la mémoire. Vous pouvez également utiliser reserve() pour provoquer une réaffectation à un montant précis si vous savez que vous allez être stocker une grande chaîne dans la variable à une date ultérieure.

Comme d'autres l'ont dit, aussi loin que vos exemples vont, vous devriez vraiment favoriser l'initialisation sur d'autres approches pour éviter la création d'objets temporaires.

Création d'une chaîne directement dans le tas est généralement pas une bonne idée, tout comme la création de types de base. Il ne vaut pas puisque l'objet peut facilement rester sur la pile et il a tout l'opérateur constructeur de copie et l'attribution nécessaire pour obtenir une copie efficace.

Le std:. Chaîne elle-même a un tampon en tas qui peut être partagée par plusieurs chaînes en fonction de la mise en œuvre

Pour intsance, avec la mise en œuvre de la STL de Microsoft, vous pouvez le faire:

string a = "Hello!";
string b = a;

Et les deux chaînes partagerait le même tampon jusqu'à ce que vous l'avez modifié:

a = "Something else!";

Voilà pourquoi il était très mauvais pour stocker les c_str () pour ce dernier; c_str () garentee seulement la validité jusqu'à ce qu'un autre appel à cet objet de chaîne est faite.

Cela a conduit à des bugs désagréables d'accès concurrentiel très exigeant cette fonctionnalité de partage pour être désactivé avec définir si vous les avez utilisés dans une application multithread

Très probablement

   string a("hello!");

est plus rapide que toute autre chose.

Vous venez de Java, non? En C ++, les objets sont traités de la même (dans la plupart des moyens) que les types de valeur de base. Les objets peuvent vivre dans la pile ou dans le stockage statique, et être passé par valeur. Lorsque vous déclarez une chaîne dans une fonction, qui prévoit d'allouer sur la pile mais d'octets l'objet chaîne prend. L'objet chaîne elle-même n'utilise la mémoire dynamique pour stocker les caractères réels, mais qui est transparent pour vous. L'autre chose à retenir est que lorsque la fonction sort et la chaîne que vous déclarée ne se portée, toute la mémoire utilisée, il est libéré. Pas besoin de la collecte des ordures (RAII est votre meilleur ami).

Dans votre exemple:

string a;
a = "less"; 
a = "moreeeeeee";

Cela met un bloc de mémoire sur la pile et le nomme un, le constructeur est appelé et est initialisé à une chaîne vide. Le compilateur stocke les octets pour « moins » et « moreeeeeee » (je pense) la section .rdata de votre exe. String aura quelques champs, comme un champ de longueur et un char * (je simplifie grandement). Lorsque vous attribuez la méthode « moins » à un, l'opérateur = () est appelée. Il alloue dynamiquement la mémoire pour stocker la valeur d'entrée, puis le copie. Lorsque vous attribuez plus tard « moreeeeeee » à un, l'opérateur = () est à nouveau appelé et il redistribue assez de mémoire pour maintenir la nouvelle valeur si nécessaire, puis recopié dans la mémoire tampon interne.

Quand les sorties de périmètre d'une chaîne, la chaîne destructor est appelée et la mémoire qui a été allouée dynamiquement pour contenir les caractères réels est libéré. Ensuite, le pointeur de la pile est décrémenté et la mémoire qui a occupé n'est plus « sur » la pile.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top