FAQ C++Consultez toutes les FAQ

Nombre d'auteurs : 35, nombre de questions : 368, dernière mise à jour : 23 mai 2017  Ajouter une question

 

Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums de http://www.developpez.com et de l'expérience personnelle des auteurs.

Je tiens à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes ; les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur ou si vous souhaitez devenir rédacteur, lisez ceci.

Sur ce, nous vous souhaitons une bonne lecture.


SommaireEntrées / sorties avec les flux (35)
précédent sommaire suivant
 

En plus de faire un retour à la ligne en ajoutant un caractère '\n', std::endl purge le buffer de sortie et force ainsi son écriture en appelant ostream::flush() (cela a le même fonctionnement que la fonction fflush() du C).
Les deux lignes de code suivantes sont donc équivalentes :

Code c++ : Sélectionner tout
1
2
std::cout << "coucou" << std::endl; 
std::cout << "coucou\n" << std::flush;
Il faut donc être prudent avec son utilisation, notamment avec les fichiers, car une opération de flush n'est pas gratuite. Son utilisation fréquente peut même sérieusement grever les performances en annulant tous les bénéfices d'une écriture bufferisée.

Mis à jour le 18 avril 2005 Aurelien.Regat-Barrel LFE

Pour injecter un objet de type quelconque dans un flux de sortie, il suffit de définir un opérateur << prenant en paramètre le flux, ainsi que l'objet à injecter.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <ostream> 
#include <string> 
  
class MaClasse 
{ 
    friend std::ostream& operator <<(std::ostream&, const MaClasse&); 
  
private : 
  
    int Integer; 
    std::string String; 
}; 
  
std::ostream& operator <<(std::ostream& Stream, const MaClasse& Obj) 
{ 
    Stream << Obj.Integer << " " << Obj.String; 
    return Stream; // N'oubliez pas de renvoyer le flux, afin de pouvoir chaîner les appels 
} 
  
MaClasse Obj; 
std::cout << "Ma classe : " << Obj << std::endl;
Notez qu'ici notre surcharge d'opérateur est déclarée amie de la classe, car elle a besoin d'accéder aux données privées de celle-ci. Cependant ce n'est pas obligatoire, notamment si les accesseurs adéquats ont été prévus dans la classe.

Le fonctionnement est le même pour l'extraction de valeurs à partir d'un flux d'entrée, via l'opérateur >> :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <istream> 
#include <string> 
  
class MaClasse 
{ 
    friend std::istream& operator >>(std::istream&, MaClasse&); 
  
private : 
  
    int Integer; 
    std::string String; 
}; 
  
std::istream& operator >>(std::istream& Stream, MaClasse& Obj) 
{ 
    Stream >> Obj.Integer >> Obj.String; 
    return Stream; // N'oubliez pas de renvoyer le flux, afin de pouvoir chaîner les appels 
} 
  
MaClasse Obj; 
std::cin >> Obj;
Le fait de prendre en paramètre un ostream ou un istream permettra d'utiliser nos surcharges avec n'importe
quel type de flux (stringstream, fstream.) puisque ceux-ci dérivent tous des classes ostream (pour les flux d'entrée) et istream (pour les flux de sortie).

Notez bien que ce genre de surcharge ne peut pas être membre de la classe, car cela impliquerait que l'opérande gauche soit l'objet et non le flux.

Enfin, si vous devez gérer l'injection et l'extraction sur une hiérarchie d'objets polymorphes, il faudra mettre en place une petite astuce (voir Comment surcharger correctement l'opérateur << pour afficher des objets polymorphes ?).

Mis à jour le 3 février 2007 Laurent Gomila

La fonction membre ostringstream::str() a deux surcharges : une sans paramètre qui renvoie le contenu du flux sous forme de string, et une autre qui accepte une chaîne en paramètre pour réinitialiser le contenu du flux. Pour vider le flux il suffit donc d'appeler cette fonction avec comme paramètre la chaîne vide.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <sstream> 
#include <string> 
  
std::ostringstream oss;  
std::string Fichier[10];  
  
// Génère une suite de noms de fichiers numérotés de 0 à 9  
for (int i = 0; i < 10; ++i)  
{  
    oss.str("");  
    oss << "Image_" << i << ".bmp";  
    Fichier[i] = oss.str();  
}
Note : on est souvent tenté d'utiliser la fonction clear, mais celle-ci n'a rien à voir : elle remet à zéro les bits d'erreur du flux.

Mis à jour le 18 avril 2005 Laurent Gomila

De la même manière que dans Comment obtenir la représentation hexadécimale d'un entier ?, on peut obtenir la représentation hexadécimale d'un buffer de caractères en représentation hexadécimale à l'aide du manipulateur std::hex.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <algorithm> 
#include <iomanip> 
#include <sstream> 
#include <string> 
  
std::string array_to_string(const unsigned char* buffer, std::size_t size) 
{ 
    std::ostringstream oss; 
  
    // Afficher en hexadécimal et en majuscule 
    oss << std::hex << std::uppercase; 
  
    // Injecter tous les caractères sous forme d'entiers dans le flux 
    std::copy(buffer, buffer + size, std::ostream_iterator<int>(oss, "")); 
  
    return oss.str(); 
}
On utilise ici std::copy et std::ostream_iterator<int> pour envoyer les éléments du buffer un à un au flux, sous forme d'entiers. Le flux étant en mode hexadécimal, ceux-ci seront chacun correctement convertis. Le manipulateur std::uppercase sert quant à lui à obtenir une représentation des lettres hexadécimales en majuscule.

Si l'on souhaite personnaliser la représentation hexadécimale, on peut développer la boucle de copie comme ceci :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <algorithm> 
#include <iomanip> 
#include <sstream> 
#include <string> 
  
std::string array_to_string(const unsigned char* buffer, std::size_t size) 
{ 
    std::ostringstream oss; 
  
    // Afficher en hexadécimal et en majuscule 
    oss << std::hex << std::uppercase; 
  
    // Remplir les blancs avec des zéros 
    oss << std::setfill('0'); 
  
    for (std::size_t i = 0; i < size; ++i) 
    { 
        // Séparer chaque octet par un espace 
        if (i != 0) 
            oss << ' '; 
  
        // Afficher sa valeur hexadécimale précédée de "0x" 
        // setw(2) permet de forcer l'affichage à 2 caractères 
        oss << "0x" << std::setw(2) << static_cast<int>(buffer[i]); 
    } 
  
    return oss.str(); 
} 
  
int main() 
{ 
    const std::size_t size = 5; 
    const unsigned char data[size] = {0x0C, 0x10, 0xFF, 0x00, 0xDA}; 
    std::cout << array_to_string(data, size) << '\n'; 
}
Le code ci-dessus produit le résultat suivant :
0x0C 0x10 0xFF 0x00 0xDA

Mis à jour le 17 octobre 2005 Aurelien.Regat-Barrel Laurent Gomila

Afin de rediriger l'entrée ou l'une des sorties standard (cin, cout, cerr ou clog) vers un autre flux, il suffit de réaffecter leur streambuf (le streambuf est l'objet qui fait le lien entre le flux et ce vers quoi il lit / écrit).

Afin d'accéder au streambuf d'un flux, il faut utiliser la fonction membre rdbuf (ses deux surcharges pour être précis) :

Code c++ : Sélectionner tout
1
2
3
4
5
// Récupérer le streambuf d'un flux 
std::streambuf* std::ios::rdbuf(); 
  
// Changer le streambuf d'un flux 
void std::ios::rdbuf(std::streambuf*);
Ainsi, pour rediriger cout vers un fichier par exemple, il suffira de procéder ainsi :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <fstream> 
#include <iostream> 
  
int main() 
{ 
    std::cout << "Affichage sur la console"<< std::endl; 
  
    // Redirection de std::cout vers le fichier Toto.txt 
    std::ofstream Out("Toto.txt"); 
    std::streambuf* OldBuf = std::cout.rdbuf(Out.rdbuf()); 
  
    std::cout << "Affichage dans Toto.txt"<< std::endl; 
  
    // Restauration du streambuf initial de std::cout (affichage sur la console) 
    std::cout.rdbuf(OldBuf); 
  
    return 0; 
}
De cette manière vous n'êtes pas limité, cette méthode permet de rediriger n'importe quel flux vers n'importe quel autre.

À noter également que si vous ne souhaitez effectuer une telle redirection que ponctuellement et pour toute la durée de l'exécution, vous pouvez plus simplement utiliser une redirection au niveau de votre invite de commande (avec une syntaxe dépendante de l'OS) :
>MonProgramme.exe > Toto.txt

Mis à jour le 3 février 2007 Cline

La STL propose deux classes pour itérer sur les flux : istream_iterator et ostream_iterator (définies dans l'en-tête <iterator>).

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
std::vector<int> Tab; 
Tab.push_back(1); 
Tab.push_back(3); 
Tab.push_back(6); 
  
// Plus besoin de boucle pour afficher les éléments du tableau : 
std::copy(Tab.begin(), Tab.end(), std::ostream_iterator<int>(std::cout, "\n"));
Dans cet exemple, l'itérateur va parcourir les éléments du tableau un à un et utiliser l'opérateur << pour les injecter dans le flux passé en paramètre, en l'occurrence ici std::cout.
Le second paramètre de l'itérateur est simplement le délimiteur qui sera placé entre chaque élément.

Vous pouvez également utiliser les stream_iterator avec des classes quelconques, pourvu que leur opérateur << soit correctement défini.

Cela marche aussi dans l'autre sens : avec un std::istream_iterator vous pourrez extraire des valeurs / objets d'un flux les unes après les autres, et les placer par exemple dans un tableau.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
// On va extraire à partir d'un fichier, par exemple 
std::ifstream File("Data.txt"); 
  
// Méthode 1 : std::copy 
std::vector<MaClasse> Tab1; 
std::copy(istream_iterator<MaClasse>(File), istream_iterator<MaClasse>(), std::back_inserter(Tab1)); 
// std::back_inserter va remplacer les affectations par des push_back 
  
// Méthode 2 : utiliser le constructeur prenant une paire d'itérateurs 
std::vector<MaClasse> Tab2(istream_iterator<MaClasse>(File), (istream_iterator<MaClasse>()));
Un istream_iterator construit par défaut (comme le second ici) représentera toujours la fin du flux.

La paire de parenthèses a priori inutile autour du second istream_iterator est nécessaire : sans cela le compilateur croirait à une déclaration de fonction. Pour éviter cela vous pouvez également déclarer les variables correspondant aux itérateurs en dehors de l'appel :

Code c++ : Sélectionner tout
1
2
3
istream_iterator<MaClasse> Begin(File); 
istream_iterator<MaClasse> End; 
std::vector<MaClasse> Tab2(Begin, End);
Pour les traitements sur des flux binaires (i.e. ouvertes avec l'option binary), il vaudra mieux utiliser std::istreambuf_iterator, qui n'appliquera pas de traitement spécial aux caractères spéciaux.

Mis à jour le 3 février 2007 Laurent Gomila

Pour que le code suivant compile :

Code c++ : Sélectionner tout
1
2
3
4
5
istringstream s( "10" ); 
int n; 
if ( s >> n ) 
{ 
}
Le compilateur cherche à convertir l'expression s >> n en un booléen. Il commence par rechercher un opérateur applicable et trouve celui hérité de istream qui accepte un int. Cela revient donc à avoir le code suivant :

Code c++ : Sélectionner tout
1
2
3
4
5
istringstream s( "10" ); 
int n; 
if ( s.operator >>( n ) ) 
{ 
}
Cet opérateur retourne une référence sur le flux utilisé, c'est-à-dire ici la variable s.
Le compilateur essaye alors de convertir cette référence (qui correspond à s après l'appel de l'opérateur >>, donc après avoir tenté la conversion en int) en booléen. istream ne définit pas d'opérateur de conversion vers bool, mais une conversion implicite est possible grâce à l'opérateur operator void*(). Ce dernier retourne un pointeur nul si les indicateurs d'échec du flux sont positionnés, ce qui est le cas en cas d'échec de conversion. L'ensemble du processus effectué par le compilateur correspond donc aux appels explicites suivants :

Code c++ : Sélectionner tout
1
2
3
4
5
istringstream s( "10" ); 
int n; 
if ( s.operator >>( n ).operator void*() != 0 ) 
{ 
}

Mis à jour le 19 octobre 2004 Aurelien.Regat-Barrel

Un tampon (buffer en anglais) est un moyen d'assouplir l'utilisation d'un système lent par un autre plus rapide.

Au lieu d'accéder directement au système lent, l'utilisateur va simplement compléter une liste de demandes en attente.
Ainsi, l'utilisateur n'a plus besoin d'attendre le traitement, et peut poursuivre son activité.

En C++, le flux de sortie standard, std::cout, utilise comme tampon un tableau de caractères (caché dans la bibliothèque standard).
Cela, parce que l'image affichée à l'écran n'est pas disponible en permanence. Il en est de même avec un fichier.
On retrouve cette idée avec les files d'attente d'un péage, ou celles à l'entrée d'un routeur.

Le mécanisme d'un tampon est assez simple :
À chaque occasion qui se présente, le système lent regarde si le tampon contient au moins une entrée. Si c'est le cas, il en retire une et la traite.
Laquelle est tirée en premier dépend du besoin, mais c'est souvent la plus ancienne.

En général, un tampon est limité en taille. Quand il est plein, deux traitements sont possibles : rejeter les nouvelles demandes ou mettre en attente l'utilisateur.
Un routeur rejette les demandes, ce qui provoque des messages d'erreur tels que "Cette page ne répond pas."
Le péage va plutot prendre son temps, l'utilisateur se retrouve à attendre (c'est un bouchon).

Les tampons fonctionnent bien quand les demandes arrivent en rafales : une série de demandes, puis rien pendant un temps largement suffisant pour toutes les traiter.

Mis à jour le 23 mai 2017 ternel

Un tampon permet de réduire l'impact de la lenteur d'accès à une ressource, mais désynchronise celle-ci de l'utilisateur.
Purger un tampon (flush en anglais) consiste à demander à la ressource de traiter toutes les demandes et attendre qu'il l'ait fait.

Cela permet de resynchroniser l'ensemble, par exemple pour s'assurer que le système lent est bien dans l'état demandé.

Cela signifie ralentir le programme, certes, mais c'est un bon moyen de synchroniser des choses qui ne le sont pas de base, comme le flux de sortie standard et celui de saisie, ou encore les flux de sortie standard et d'erreur: std::cout et std::cerr.

En C++, il y a deux manipulateurs de flux de sortie qui sont utilisables pour cela: std::flush et std::endl.
La seule différence entre les deux, c'est que endl ajoute un retour à la ligne avant de purger le buffer.

Ainsi cout << 2 << flush affiche 2 immédiatement, parce que cela ajoute 2 au bout du buffer puis purge celui-ci.
De même, cout << endl se comporte comme cout << '\n' << flush.

Mis à jour le 23 mai 2017 ternel

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2017 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

 
Contacter le responsable de la rubrique C++