Les conséquences et les avantages/inconvénients de vider le flux en c++
J'ai récemment lu un article qui indiquait que l'utilisation de n
était préférable à l'utilisation de std::endl
Car endl
vidait également le flux.
Mais lorsque j'ai cherché un peu plus d'informations sur le sujet, j'ai trouvé un site qui dit:
Si vous êtes dans une situation où vous devez éviter la mise en mémoire tampon, vous pouvez utiliser std:: endl au lieu de ‘n’
Maintenant, voici ma question: Dans quelle situation est-il préférable pas pour écrire dans le buffer? Parce que je n'ai vu qu' les avantages de cette technique. N'est-il pas aussi plus sûr d'écrire dans le tampon? Car il est plus petit qu'un disque dur, il sera remplacé plus rapidement que les données stockées sur le HD (je ne suis pas sûr si c'est vrai).
5 réponses
Lorsque la mise en mémoire tampon se produit, vous n'aurez aucune garantie que les données soient immédiatement reçues avant qu'un vidage ne se produise. Dans des circonstances particulières, vous pourriez rencontrer un mauvais ordre de sortie et / ou une perte d'informations / de données de débogage, par exemple
int main() {
std::cout << "This text is quite nice and might as well be buffered";
raise(SIGSEGV); // Oh dear.. segmentation violation
std::cout << std::endl;
}
Sortie:
bash: line 7: 22235 Segmentation fault (core dumped) ./a.out
Ce qui précède n'imprime aucun texte car la mise en mémoire tampon a empêché l'affichage de la bonne sortie.
Maintenant, si vous ajoutez simplement un rinçage std::endl
à la fin du tampon c'est ce que vous obtenez
int main() {
std::cout << "This text is quite nice and might as well be buffered" << std::endl;
raise(SIGSEGV); // Oh dear.. segmentation violation
std::cout << std::endl;
}
Sortie:
This text is quite nice and might as well be buffered
bash: line 7: 22444 Segmentation fault (core dumped) ./a.out
Cette fois, la sortie est visible avant fin du programme.
Les Implications de ce fait sont multiples. Purement spéculatif: si les données étaient liées à un journal de Serveur, votre application aurait pu être plantée avant la journalisation réelle.
Il sera préférable dans toute situation dans laquelle vous voulez que la sortie apparaisse exactement quand elle était censée apparaître.
Un exemple simple:
#include <iostream>
int main() {
std::cout << "Please enter your name: " << std::endl;
std::string name;
std::cin >> name;
...
}
Avec la mise en mémoire tampon, aucun texte n'apparaîtra à l'écran avant que l'utilisateur ne soit censé taper son nom, de sorte que l'Utilisateur sera confus. (Notez qu'en fait, il peut être vraiment difficile ou impossible d'exécuter cet exemple avec la mise en mémoire tampon entièrement activée, car C++ peut prendre des mesures spéciales pour vider std::cout
avant toute entrée de std::cin
, voir Pourquoi avons-nous besoin de l'attacher std::cin et std::cout?. Mais ceci est juste un exemple théorique: dans le cas où la mise en mémoire tampon est entièrement activée, l'utilisateur ne verra pas l'invite.)
Une telle situation peut se produire de temps en temps, bien que ce ne soit peut-être pas très souvent. Envisagez d'écrire dans un tuyau pour interagir avec un autre processus. Ou même si votre programme écrit dans le fichier journal et vous personnellement, regardez dans le fichier journal de temps en temps pour voir comment il fonctionne, - - - en cas de mise en mémoire tampon, vous ne verrez généralement pas la sortie qui a été imprimée à partir du programme, mais reste encore dans le tampon.
Une autre situation importante à prendre en compte - - - si votre programme se bloque gravement, le contenu du tampon peut ne pas se terminer sur le disque dur du tout. (Je m'attends à ce que les destructeurs de flux vident le tampon, mais un crash peut être si grave qu'aucun destructeur ne sera appelé du tout.)
, Il est préférable de vider la mémoire tampon si vous besoin la cible de votre flux pour recevoir les données avant que le flux est fermé.
Un exemple réel serait un journal d'application, écrit à partir d'un flux toujours ouvert... Vous pouvez regarder ce journal, alors que le programme est toujours en cours d'exécution.
D'abord, un peu d'histoire révisionniste.
Dans l'ancien temps, quand tout le monde utilisait la bibliothèque stdio.h
pour faire des e/s, le texte visualisé de manière interactive était généralement ligne mise en mémoire tampon (ou même unbuffered), et le texte qui ne l'était pas était entièrement mis en mémoire tampon. Donc, si vous produisez '\n'
dans le flux, cela ferait "toujours" la bonne chose: les utilisateurs de lignes regardaient se vider et voir immédiatement, et les lignes qui étaient déversées dans le fichier sont tamponnées pour un maximum performance.
Malheureusement, ce n'est pas toujours la bonne chose; le runtime ne peut pas toujours prédire comment les utilisateurs veulent réellement afficher la sortie de votre programme. Un piège commun est la redirection STDOUT
- les gens s'habituent à exécuter leur programme dans une console et à voir la sortie (avec son comportement en tampon de ligne) dans la console, puis pour une raison quelconque (par exemple travail de longue durée) ils décident de rediriger STDOUT
vers un fichier, et sont rapidement surpris par le fait que la sortie ligne plus longue tamponnée.
J'ai vu des semaines de temps de supercalculateur gaspillé pour cette raison; la sortie était assez rare pour que la mise en mémoire tampon empêche quiconque de pouvoir dire comment le travail progressait.
La bibliothèqueiostream
de C++, cependant, a été conçue pour rendre facile de faire la bonne chose ici. Sauf lors de la synchronisation avec stdio
, il ne fait pas cette drôle de chose "peut-être en ligne tamponnée peut-être entièrement tamponnée". Il utilise toujours la mise en mémoire tampon complète (sauf lorsque vous faites des choses non tamponnées, bien sûr), et si vous voulez que les choses soient vidées en nouvelle ligne, vous le faites explicitement .
Donc, si vous videz un tas de texte formaté dans un fichier que les gens ne regarderont pas tant que ce n'est pas fait, vous écrivez \n
pour vos sauts de ligne.
Mais si vous écrivez du texte que les gens pourraient vouloir regarder pendant que vous l'écrivez, vous utilisez std::endl
pour vos sauts de ligne, et il s'affiche immédiatement. Et si vous écrivez plusieurs lignes à la fois, vous peut même faire mieux: utilisez '\n'
pour les sauts de ligne intermédiaires et std::endl
pour le dernier (ou '\n'
et std::flush
). Bien que dans ce paramètre, les performances n'aient généralement pas d'importance, il est généralement bon d'utiliser std::endl
pour tous les sauts de ligne.
J'espère que vous avez perdu le lien vers ce site que vous avez trouvé. std::endl
n'évite pas la mise en mémoire tampon. Il rince tout ce qui est dans le tampon. Si vous devez éviter la mise en mémoire tampon, utilisez setf(ios_base::unitbuf)
. Cela définit le flux à vider après chaque insertion. C'est le paramètre par défaut pour std::clog
. La raison pour cela est que moins les choses sont conservées dans le tampon, plus les chances que les données critiques aient été écrites dans le flux lorsque le programme se bloque sont grandes.
Le rinçage est également important pour programmes interactifs: si vous écrivez une invite à std::cout
, C'est une bonne chose si cette invite apparaît sur l'écran avant que le programme ne commence à attendre l'entrée. Cela se fait automatiquement lorsque vous utilisez std::cout
et std::cin
, sauf si vous avez foiré avec les paramètres de synchronisation.
Beaucoup de programmeurs semblent utiliser {[0] } comme une façon élégante d'orthographe '\n'
, mais ce n'est pas le cas. Vous n'avez pas besoin de vider le tampon de sortie chaque fois que vous y écrivez quelque chose. Laissez le système d'exploitation et la bibliothèque standard faire leur travail; ils vont prendre soin d'obtenir la sortie à l'endroit approprié en temps opportun. Un simple std::cout << '\n';
est tout ce qui est nécessaire pour mettre une nouvelle ligne dans la sortie, et tôt ou tard, cela apparaîtra sur l'écran. Si vous devez l'Afficher maintenant, généralement parce que vous avez écrit toute la sortie pour le moment et que vous ne voulez pas laisser les informations affichées incomplètes, utilisez std::endl
après la dernière ligne de la sortie.