585 votes

Comment effacer une variable stringstream ?

J'ai déjà essayé plusieurs choses,

std::stringstream m;
m.empty();
m.clear();

qui ne fonctionnent pas.

918voto

Wilka Points 13239

Pour tous les types de bibliothèque standard, la fonction membre empty() est une requête, pas une commande, c'est-à-dire que cela signifie "êtes-vous vide ?" et non "veuillez jeter votre contenu".

La fonction membre clear() est héritée de ios et est utilisée pour effacer l'état d'erreur du flux, par exemple si un flux de fichier a l'état d'erreur défini sur eofbit (fin de fichier), alors appeler clear() rétablira l'état d'erreur à goodbit (pas d'erreur).

Pour effacer le contenu d'un stringstream, utiliser :

m.str("");

est correct, bien que l'utilisation de :

m.str(std::string());

soit techniquement plus efficace, car vous évitez d'invoquer le constructeur std::string prenant const char*. Mais tout compilateur de nos jours devrait être capable de générer le même code dans les deux cas - donc je choisirais simplement ce qui est le plus lisible.

134 votes

Voici ce qui se passe lorsque vous oubliez la partie "clear()". stackoverflow.com/q/2848087/635549

0 votes

Pourquoi est-ce que m.str() renvoie les valeurs de chaîne mais ne vide pas le flux?

8 votes

@KshitijBanerjee Je pense qu'en C++ m.str() et m.str("") sont deux fonctions différentes. m.str() invoque une fonction qui n'attendait aucun paramètre alors que m.str("") invoquera la fonction qui accepte un paramètre const char*. m.str() pourrait avoir été implémenté comme une fonction get qui renvoie la chaîne de caractères alors que m.str("") pourrait avoir été implémenté comme une fonction set.

72voto

Nikos Athanasiou Points 7015

Vous pouvez effacer l'état d'erreur et vider le stringstream en une seule ligne

std::stringstream().swap(m); // échanger m avec un stringstream construit par défaut

Cela réinitialise efficacement m à un état construit par défaut, ce qui signifie que cela supprime en réalité les tampons alloués par le stringstream et réinitialise l'état d'erreur. Voici une preuve expérimentale :

int main ()
{
    std::string payload(16, 'x');

    std::stringstream *ss = new std::stringstream; // Crée une fuite de mémoire
    (*ss) << payload;                              // Fuite de mémoire supplémentaire

    // Maintenant choisissez une manière de "vider" un stringstream
    //std::stringstream().swap(*ss); // Méthode 1
    //ss->str(std::string());        // Méthode 2

    std::cout << "fin" << std::endl;
}

Démonstration

Lorsque la démonstration est compilée avec un vérificateur d'adresse, l'utilisation de la mémoire est révélée :

=================================================================
==10415==ERROR: LeakSanitizer: detected memory leaks

Fuite directe de 392 octet(s) dans 1 objet(s) alloué(s) provenant de :
    #0 0x510ae8 in operator new(unsigned long) (/tmp/1637178326.0089633/a.out+0x510ae8)
    #1 0x514e80 in main (/tmp/1637178326.0089633/a.out+0x514e80)
    #2 0x7f3079ffb82f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291

Fuite indirecte de 513 octet(s) dans 1 objet(s) alloué(s) provenant de :
    #0 0x510ae8 in operator new(unsigned long) (/tmp/1637178326.0089633/a.out+0x510ae8)
    #1 0x7f307b03a25c in std::__cxx11::basic_string, std::allocator >::reserve(unsigned long) (/usr/local/lib64/libstdc++.so.6+0x13725c)
    #2 0x603000000010  ()

RÉSUMÉ: AddressSanitizer: 905 octet(s) fuit(s) dans 2 allocation(s).

Assez raide si vous me demandez. Pour contenir seulement 16 octets de charge utile, nous avons dépensé 905 octets ... les stringstream ne sont pas un jouet. La mémoire est allouée en deux parties :

  • Le stringstream construit (392 octets)
  • Le tampon supplémentaire nécessaire pour la charge utile (513 octets). La taille superflue est liée à la stratégie d'allocation choisie par le stringstream et pour les charges utiles <= 8 octets, des blocs à l'intérieur de l'objet initial peuvent être utilisés.

Si vous activez la méthode 1 (celle indiquée dans cette réponse), les 513 octets supplémentaires (charge utile) sont récupérés, car le stringstream est effectivement effacé.

Si vous activez la méthode 2 comme suggéré dans les commentaires ou d'autres réponses, vous pouvez voir que les 905 octets sont tous utilisés au moment où nous quittons.

En termes de sémantique du programme, on peut simplement se soucier du fait que le stringstream "paraît" et "se comporte" comme vide, de manière similaire à la façon dont un vector::clear peut laisser la capacité inchangée mais rendre le vecteur vide pour l'utilisateur (bien sûr, le vecteur ne dépenserait que 16 octets ici). Étant donné l'allocation de mémoire requise par le stringstream, je peux imaginer que cette approche est souvent plus rapide. L'objectif principal de cette réponse est en fait d'effacer le stringstream, étant donné que la consommation de mémoire qui l'accompagne n'est pas une plaisanterie. Selon votre cas d'utilisation (nombre de stringstream, données qu'ils contiennent, fréquence de l'effacement), vous pouvez choisir la meilleure approche.

Enfin, notez qu'il est rarement utile d'effacer le stringstream sans effacer l'état d'erreur et tout l'état hérité. La ligne unique dans cette réponse fait les deux.

5 votes

Ceci est la manière la plus efficace et la plus élégante de le faire par rapport à toutes les autres réponses ici. Cependant, std :: stringstream :: swap est une fonctionnalité c ++ 11 et cette solution ne fonctionne pas pour les compilateurs c ++ 11 antérieurs.

5 votes

Fonctionnalité toujours manquante dans GNU g++ v4.8, consultez stackoverflow.com/questions/24429441/…

9 votes

@101010: En quoi l'échange est-il préférable à l'attribution par déplacement ?

44voto

jerron Points 91

Cela devrait être le moyen le plus fiable quel que soit le compilateur :

m=std::stringstream();

2 votes

Ceci est meilleur à mon avis car m.str(""); a causé mon stringstream à rester coincé avec cette valeur vide quoi que j'essaie. Mais en utilisant cela, je n'ai pas ce problème.

3 votes

Je suis tombé sur le même problème, pour moi mm.clear(); mm.str(""); a fonctionné. (pas de C++11, sinon swap serait meilleur).

1 votes

@hochl: Pourquoi swap serait-il meilleur que l'affectation par déplacement ?

38voto

m.str("");

semble fonctionner.

3 votes

Cela serait effacé par .clear() qui est spécifié dans la PO.

14voto

TimoK Points 33

Je le déclare toujours dans la portée :

{
    std::stringstream ss;
    ss << "quoi";
}

{
    std::stringstream ss;
    ss << "le";
}

{
    std::stringstream ss;
    ss << "diable";
}

1 votes

Est-ce mieux que de vider le stringstream?

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