100 votes

Comment fonctionne std::flush ?

Quelqu'un peut-il expliquer (de préférence en anglais simple) comment std::flush fonctionne ?

  • Qu'est-ce que c'est ?
  • Quand tirer la chasse d'eau d'un ruisseau ?
  • Pourquoi est-ce important ?

Merci.

150voto

Dietmar Kühl Points 70604

Comme il n'a pas été répondu ce que std::flush 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 de manière simple, on peut 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, vous trouverez également des explications ci-dessous).

La classe stream surcharge les opérateurs de sortie en prenant des opérateurs de cette forme, c'est-à-dire qu'il existe une fonction membre prenant un manipulateur comme 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;
}

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 déclarations suivantes sont équivalentes :

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

Maintenant, std::flush() est en soi assez simple : Tout ce qu'il fait est d'appeler std::ostream::flush() c'est-à-dire que vous pouvez envisager que sa mise en œuvre ressemble à quelque chose comme ceci :

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

Le site std::ostream::flush() appelle techniquement la fonction std::streambuf::pubsync() sur le tampon de flux (le cas échéant) qui est 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 vers la destination externe lorsque le tampon utilisé déborderait 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 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 qu'en utilisant std::flush fait en sorte que le tampon de flux vide son tampon de sortie. Par exemple, lorsque des données sont écrites sur une console, le vidage fait apparaître les caractères à cet endroit sur la console.

Cela peut soulever la question : Pourquoi les caractères ne sont-ils pas écrits immédiatement ? 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'un nombre raisonnable de caractères est essentiellement identique à celui nécessaire à l'écriture d'un seul caractère. 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 sont écrits en à peu près le même temps qu'un seul caractère. Ainsi, la mise en mémoire tampon des caractères avant de les envoyer en utilisant un tampon dépendant des détails de la destination externe peut constituer une énorme amélioration des performances.

Ce qui précède devrait répondre à deux de vos trois questions. La question restante est la suivante : quand faut-il tirer la chasse d'eau d'un ruisseau ? La réponse est la suivante : Quand les caractères doivent être écrits vers la destination externe ! Cela peut être à la fin de l'écriture d'un fichier (la fermeture d'un fichier vide implicitement le tampon) ou immédiatement avant de demander l'entrée de l'utilisateur (notez que la commande std::cout est automatiquement vidée lors de la lecture de std::cin como std::cout es std::istream::tie() d à std::cin ). Bien qu'il puisse y avoir quelques occasions où vous voulez explicitement vider un flux, je les trouve assez rares.

Enfin, j'ai promis de donner une image complète de ce que std::flush est en fait : Les flux sont des modèles de classe capables de traiter différents types de caractères (en pratique, ils fonctionnent avec des char y wchar_t (les faire fonctionner 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 ça n'a pas vraiment d'importance : Le compilateur déduit automatiquement les arguments du template. 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 déduira pas les arguments de modèle.

40voto

Kerrek SB Points 194696

Par défaut, std::cout est mise 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 flux). Parfois, vous voulez vous assurer que l'impression a lieu immédiatement, et vous devez vider manuellement.

Par exemple, supposons que vous souhaitiez signaler 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 verrez pas le résultat avant très longtemps.

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

28voto

Neil Anderson Points 281

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

#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, fait une pause, puis imprime les lignes 2 et 3. Maintenant, supprimez l'appel flush et exécutez à nouveau le programme : vous remarquerez que le programme marque une pause, puis imprime les 3 lignes en même temps. La première ligne est mise en mémoire tampon avant la pause du programme, mais comme le tampon n'est jamais vidé, la ligne 1 n'est pas sortie avant l'appel endl de la ligne 2.

8voto

Lee Meador Points 7902

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

Lorsque vous videz le flux, cela indique aux bibliothèques du langage, au système d'exploitation et au matériel que vous voulez que tous les caractères que vous avez émis jusqu'à présent soient forcés jusqu'au stockage. Théoriquement, après un "flush", vous pourriez jeter le cordon d'alimentation hors du mur et ces caractères seraient toujours stockés en toute sécurité.

Je dois préciser que les personnes qui écrivent les pilotes de systèmes d'exploitation ou celles qui conçoivent les lecteurs de disques sont libres d'utiliser 'flush' comme suggestion et qu'elles n'écrivent peut-être pas vraiment les caractères. Même lorsque la sortie est fermée, ils peuvent attendre un certain temps avant de les enregistrer. (Rappelez-vous que le système d'exploitation fait toutes sortes de choses en même temps et qu'il peut être plus efficace d'attendre une seconde ou deux pour traiter vos octets).

Donc un flush est une sorte de point de contrôle.

Un autre exemple : Si la sortie est destinée à l'affichage de la console, un flush s'assurera que les caractères sont bien affichés à l'endroit où l'utilisateur peut 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 qu'elle reste coincée dans un tampon interne quelque part, l'utilisateur ne sait pas quoi taper en réponse. C'est donc un cas où le flush est important.

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