web-dev-qa-db-fra.com

Comment fonctionne std :: flush?

Quelqu'un peut-il s'il vous plaît expliquer (de préférence en anglais simple) comment std::flush travaux?

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

Merci.

66
Dasaru

Comme il n'a pas été répondu quoi _std::flush_ se trouve être, 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_ de la signature

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

La réalité est cependant un peu plus complexe (si cela vous intéresse, les explications ci-dessous sont également expliquées).

Les opérateurs de sortie de surcharge de classe de flux prenant des opérateurs de cette forme, c’est-à-dire qu’une fonction membre prend un manipulateur en tant qu’argument. L'opérateur de sortie appelle le manipulateur avec l'objet lui-même:

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

En d'autres termes, lorsque vous "exportez" _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 suffit d'appeler std::ostream::flush(), c'est-à-dire que vous pouvez imaginer que son implémentation ressemble à ceci:

_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 flux (le cas échéant) associé au flux: le tampon de flux est responsable de la mise en mémoire tampon des caractères et de l'envoi des caractères à la destination externe lorsque le le tampon utilisé déborderait ou lorsque la représentation interne devrait être synchronisée avec la destination externe, c'est-à-dire lorsque les données doivent être vidées. Sur un flux 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. C'est-à-dire que l'utilisation de _std::flush_ force le tampon de flux à vider son tampon de sortie. Par exemple, lorsque les données sont écrites sur un vidage de la console, les caractères apparaissent à ce stade sur la console.

Cela peut soulever la question suivante: 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 pour écrire un nombre raisonnable de caractères est essentiellement identique à celui où vous écrivez un seul endroit. La quantité de caractères dépend de nombreuses caractéristiques du système d’exploitation, des systèmes de fichiers, etc., mais souvent jusqu’à 4 000 caractères environ sont écrits à peu près au même moment qu’un seul caractère. Ainsi, la mise en mémoire tampon des caractères avant de les envoyer à l'aide d'une mémoire 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 qui reste est la suivante: quand videriez-vous un flux? La réponse est: Quand les caractères doivent être écrits sur la destination externe! Cela peut être à la fin de l’écriture d’un fichier (la fermeture d’un fichier vide implicitement le tampon) ou juste avant de demander l’entrée utilisateur (notez que _std::cout_ est automatiquement vidé lors de la lecture de _std::cin_ comme _std::cout_ est std::istream::tie() 'd à _std::cin_). Bien qu'il y ait quelques occasions où vous voulez explicitement vider un flux, je le trouve assez rare.

Enfin, j'ai promis de donner une image complète de ce que _std::flush_ est réellement: Les flux sont des modèles de classe capables de traiter différents types de caractères (en pratique, ils fonctionnent avec char et _wchar_t_; les faire travailler avec d'autres personnages est assez compliqué mais faisable si vous êtes vraiment déterminé). Pour pouvoir utiliser _std::flush_ avec toutes les instanciations de flux, il s’agit d’un modèle de fonction avec une signature comme celle-ci:

_template <typename cT, typename Traits>
std::basic_ostream<cT, Traits>& std::flush(std::basic_ostream<cT, Traits>&);
_

Lorsque vous utilisez _std::flush_ immédiatement avec une instanciation de _std::basic_ostream_, cela n'a pas 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 de modèle, le compilateur ne pourra pas en déduire les arguments du modèle.

107
Dietmar Kühl

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

Par exemple, supposons que vous souhaitiez rapporter un rapport d'avancement 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 flux et le fait vider. Puisque le rinçage coûte modérément cher, std::endl ne doit pas être utilisé de manière excessive si le rinçage n’est pas expressément souhaité.

25
Kerrek SB

Voici un programme court que vous pouvez écrire pour observer ce que fait la couleur

#include <iostream>
#include <unistd.h>

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. Maintenant, supprimez l’appel de vidage et relancez le programme. Vous remarquerez que le programme se met en pause puis imprime les 3 lignes à la 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 sortie que lorsque l’appel endl de la ligne 2 est terminé.

11
Neil Anderson

Un flux est connecté à quelque chose. Dans le cas d'une sortie standard, il peut s'agir de la console/de l'écran ou il peut être redirigé vers un canal ou un fichier. Il y a beaucoup de code entre votre programme et, par exemple, le disque dur sur lequel le fichier est stocké. Par exemple, le système d'exploitation utilise un fichier quelconque ou le lecteur de disque lui-même peut mettre des données en mémoire tampon pour pouvoir les écrire sous forme de blocs de taille fixe ou tout simplement pour être plus efficaces.

Lorsque vous videz le flux, il indique aux bibliothèques de langues, au système d'exploitation et au matériel que vous souhaitez que les caractères que vous avez générés jusqu'à présent soient forcés jusqu'au stockage. Théoriquement, après un "flush", vous pourriez tirer le cordon du mur et ces personnages seraient toujours stockés en toute sécurité.

Je devrais mentionner que les personnes écrivant les pilotes os ou les concepteurs du lecteur de disque sont libres d'utiliser "flush" comme suggestion et de ne pas écrire les caractères. Même lorsque la sortie est fermée, ils peuvent attendre un moment pour les enregistrer. (N'oubliez pas que le système d'exploitation fait toutes sortes de choses en même temps et qu'il serait peut-être plus efficace d'attendre une seconde ou deux pour gérer vos octets.)

Donc, une chasse est une sorte de point de contrôle.

Un autre exemple: si la sortie est affichée sur la console, un flush s'assurera que les caractères parviennent bien à l'endroit où l'utilisateur peut les voir. C'est une chose importante à faire lorsque vous attendez une saisie au clavier. Si vous pensez que vous avez écrit une question sur la console et que sa mémoire est toujours bloquée dans un tampon interne, l'utilisateur ne sait pas quoi taper en réponse. Donc, c'est un cas où la couleur est importante.

4
Lee Meador