Comment std :: flush fonctionne-t-il?

Quelqu’un peut-il s’il vous plaît expliquer (de préférence en utilisant un anglais clair) comment std::flush fonctionne?

  • Qu’Est-ce que c’est?
  • Quand voudriez-vous vider un stream?
  • Pourquoi c’est important?

Je vous remercie.

Comme il n’a pas été répondu à ce que std::flush est, voici quelques détails sur ce que c’est réellement. std::flush est un manipulateur , c’est-à-dire une fonction avec une signature spécifique. Pour commencer simple, vous pouvez penser à std::flush d’avoir la signature

 std::ostream& std::flush(std::ostream&); 

La réalité est toutefois un peu plus complexe (si cela vous intéresse, cela est également expliqué ci-dessous).

La classe de stream surcharge les opérateurs de sortie en prenant les opérateurs de cette forme, c’est-à-dire qu’une fonction membre prend un manipulateur en argument. L’opérateur de sortie appelle le manipulateur avec l’object lui-même:

 std::ostream& std::ostream::operator<< (std::ostream& (*manip)(std::ostream&)) { (*manip)(*this); return *this; } 

C'est-à-dire que lorsque vous "sortez" std::flush avec un std::ostream , il appelle simplement la fonction correspondante, c'est-à-dire que les deux instructions suivantes sont équivalentes:

 std::cout << std::flush; std::flush(std::cout); 

Maintenant, std::flush() lui-même est assez simple: il std::ostream::flush() appeler std::ostream::flush() , c'est-à-dire que vous pouvez envisager son implémentation comme std::ostream::flush() :

 std::ostream& std::flush(std::ostream& out) { out.flush(); return out; } 

La fonction std::ostream::flush() appelle techniquement std::streambuf::pubsync() sur le tampon de stream (le cas échéant) associé au stream: Le tampon de stream est responsable de la mise en mémoire tampon des caractères et de destination externe lorsque le tampon utilisé déborde ou lorsque la représentation interne doit être synchronisée avec la destination externe, c'est-à-dire lorsque les données doivent être vidées. Sur un stream séquentiel, la synchronisation avec la destination externe signifie simplement que tous les caractères mis en mémoire tampon sont immédiatement envoyés. En d'autres std::flush , l'utilisation de std::flush provoque le vidage du tampon de sortie par le tampon de stream. Par exemple, lorsque des données sont écrites dans une console, les caractères apparaissent à ce stade sur la console.

Cela peut soulever la question: pourquoi les caractères ne sont-ils pas immédiatement écrits? La réponse simple est que l’écriture des caractères est généralement assez lente. Cependant, le temps nécessaire à l’écriture d’une quantité raisonnable de caractères est essentiellement identique à l’écriture d’une seule. La quantité de caractères dépend de nombreuses caractéristiques du système d’exploitation, des systèmes de fichiers, etc., mais jusqu’à 4 000 caractères environ sont écrits à peu près en même temps qu’un seul caractère. Ainsi, mettre en mémoire tampon les caractères avant de les envoyer en utilisant un tampon en fonction des détails de la destination externe peut constituer une amélioration considérable des performances.

Ce qui précède devrait répondre à deux de vos trois questions. La question restante est la suivante: quand voudriez-vous vider un stream? La réponse est: lorsque les caractères doivent être écrits sur la destination externe! Cela peut être à la fin de l'écriture d'un fichier (fermer un fichier implicitement videra le tampon, cependant) ou immédiatement avant de demander une entrée utilisateur (notez que std::cout est automatiquement vidé de std::cin comme std::cout est std::istream::tie() 'd en std::cin ). Bien qu'il puisse y avoir quelques occasions où vous voulez explicitement vider un stream, je les trouve assez rares.

Enfin, j'ai promis de donner une image complète de ce que std::flush est réellement: Les stream sont des modèles de classes capables de traiter différents types de caractères (en pratique, ils fonctionnent avec char et wchar_t ; faisable si vous êtes vraiment déterminé). Pour pouvoir utiliser std::flush avec toutes les instanciations de stream, il se trouve que ce soit un modèle de fonction avec une signature comme celle-ci:

 template  std::basic_ostream& std::flush(std::basic_ostream&); 

Lorsque vous utilisez std::flush immédiatement avec une instanciation de std::basic_ostream cela n'a pas vraiment d'importance: le compilateur déduit automatiquement les arguments du modèle. Cependant, dans les cas où cette fonction n'est pas mentionnée avec quelque chose facilitant la déduction des arguments du modèle, le compilateur ne pourra pas en déduire les arguments du modèle.

Par défaut, std::cout est mis en mémoire tampon et la sortie réelle n’est imprimée que lorsque le tampon est plein ou qu’une autre situation de vidage se produit (par exemple, une nouvelle ligne dans le stream). Parfois, vous voulez vous assurer que l’impression se produit immédiatement et que vous devez vider manuellement.

Par exemple, supposons que vous souhaitiez signaler un rapport de progression en imprimant un seul point:

 for (;;) { perform_expensive_operation(); std::cout << '.'; std::flush(std::cout); } 

Sans le rinçage, vous ne verriez pas la sortie pendant très longtemps.

Notez que std::endl insère une nouvelle ligne dans un stream et provoque son vidage. Comme le rinçage est relativement coûteux, std::endl ne doit pas être utilisé de manière excessive si le rinçage n'est pas expressément souhaité.

Voici un petit programme que vous pouvez écrire pour observer ce que fait le flush

 #include  #include  using namespace std; int main() { cout << "Line 1..." << flush; usleep(500000); cout << "\nLine 2" << endl; cout << "Line 3" << endl ; return 0; } 

Exécutez ce programme: vous remarquerez qu'il imprime la ligne 1, marque une pause, puis imprime les lignes 2 et 3. Supprimez maintenant l'appel flush et exécutez à nouveau le programme. Vous remarquerez que le programme se met en pause puis imprime les 3 lignes au en même temps. La première ligne est mise en mémoire tampon avant la pause du programme, mais comme la mémoire tampon n'est jamais vidée, la ligne 1 n'est pas sortie avant l'appel endl de la ligne 2.

Un stream est connecté à quelque chose. Dans le cas d’une sortie standard, cela pourrait être la console / l’écran ou il pourrait être redirigé vers un canal ou un fichier. Il y a beaucoup de code entre votre programme et, par exemple, le disque dur où le fichier est stocké. Par exemple, le système d’exploitation fait des choses avec n’importe quel fichier ou le lecteur de disque lui-même peut mettre en mémoire tampon des données pour pouvoir l’écrire dans des blocs de taille fixe ou simplement pour être plus efficace.

Lorsque vous vider le stream, il indique aux bibliothèques de langues, au système d’exploitation et au matériel que vous voulez que tous les caractères que vous avez déjà sortis soient forcés jusqu’au stockage. Théoriquement, après un «flush», vous pourriez tirer le cordon du mur et ces caractères seraient toujours stockés en toute sécurité.

Je devrais mentionner que les personnes qui écrivent les pilotes d’OS ou les personnes qui conçoivent le lecteur de disque sont libres d’utiliser la suggestion «flush» et qu’elles n’écrivent pas vraiment les caractères. Même lorsque la sortie est fermée, ils peuvent attendre un peu pour les enregistrer. (Rappelez-vous que le système d’exploitation fait toutes sortes de choses à la fois et qu’il peut être plus efficace d’attendre une seconde ou deux pour gérer vos octets.)

Donc, une couleur est une sorte de sharepoint contrôle.

Un autre exemple: si la sortie est affichée sur l’écran de la console, un vidage garantit que les personnages parviennent réellement à les voir. C’est une chose importante à faire lorsque vous attendez une saisie au clavier. Si vous pensez avoir écrit une question à la console et que celle-ci est toujours bloquée dans un tampon interne quelque part, l’utilisateur ne sait pas quoi répondre. Donc, c’est un cas où le flush est important.