10.
L’héritage multiple
class A { ... } ; class B { ... } ;
class C : public A, public B { ... } ;
F Figure 1
10.1 Conflit de noms
class A { public : affiche () ; ... } ; class B { public : affiche () ; ... } ; class HM : public A, public B { ... } ; int main()
{
HM hm;
hm.affiche() // erreur de compilation car ambigu.
}
Ecrire hm.A::affiche() ou hm.B::affiche() sinon redéfinir HM::affiche()
HM::affiche() {
A :: affiche() ; // et / ou B :: affiche() ; }
10.2 Héritage 2 fois de la même classe
Figure 2
class A { protected : int k ; ... } ; class B : public A { ... } ;
class C : public A { ... } ;
class HM : public B, public C { ... } ; // hérite 2 fois de k venant de A
void HM :: affiche() {
cout << k; // ambigu, quel k ? } A A C B HM
B
A
C
Pour éviter la duplication
class A { protected : int k ; ... } ;class B : public A virtual { ... } ; // ou public virtual A ! class C : public A virtual { ... } ;
class HM : public virtual A, public B, public C { ... } ; // hérite 1 seule fois de k venant de A
void HM :: affiche() {
cout << k; // ok
}
Figure 3
A est déclaré classe de base virtuelle. Les éléments de la classe de base virtuelle ne sont insérés qu’une seule fois dans la classe dérivée.
10.3 Appels des constructeurs.
Les constructeurs sont appelés dans l’ordre où ils sont mentionnés dans la déclaration de la classe dérivée (Pas dans l’ordre où ils apparaissent dans le constructeur).
Et les constructeurs des classes de base virtuelles sont appelés avant les constructeurs des classes de bases non virtuelles, enfin le constructeur de la classe dérivée.
Ordre d’appel des constructeurs de la classe HM
HM :: HM( .... ) : B ( ... ) , C ( ... ), A ( ... ) { ... } ; // constructeur de HM constructeur de A constructeur de B constructeur de C constructeur de HM
10.4 Une utilisation :
Pour implémenter un type abstrait de données à partir d'une structure de données fondamentales. A C B HM Classe de base virtuelle
Type abstrait de données Structure de données fondamentales Pile abstraite File abstraite Ensemble abstrait ... Tableau Liste chaînée Arbre ... Pile File Ensemble Figure 4
Le type abstrait de données ne contient que des des fonctions membres virtuelles pures. Qui sont implémentées dans la classe dérivée à l'aide d'une structure de données
fondamentales.
Class EnsembleAbstrait { public :
virtual void ajouter( int ) = 0 ; virtual void enlever ( int ) = 0 ; virtual void contient ( int ) = 0 ; } ;
Class Tableau { ....
} ;
Class Ensemble : public EnsembleAbstrait, private Tableau { public :
virtual void ajouter( int ) { } virtual void enlever ( int ) { } virtual void contient ( int ) { } } ;
Par héritage multiple de EnsembleAbstrait et Tableau ou
ou par héritage multiple de EnsembleAbstrait et Liste chaînée on fabrique une nouvelle classe qui gère un ensemble d'entier
soit la fonction f :
void f (EnsembleAbstrait &e ) {
e.ajouter() ; e.enlever() ; ...
}
10.5 En conclusion
L’héritage multiple est plaisant d’un point de vue conceptuel, mais pose d’énormes problèmes de maintenance et de conflit.
11. Les conversions de type
11.1 Par les constructeurs : constructeur de conversion
Class Complex{ ...
friend Complex operator+ (Complex, Complex ); public :
Complex( int a, int b = 0); ...
} ;
Complex x( 0, 0), y( -1, 2);
Complex z = 5 ; // z = Complex( 5, 0 );
x = y + 2 // x = operator+ ( y, Complex( 2, 0 ) )
Si on a un constructeur à un argument, il effectue automatiquement une conversion de type. Le constructeur à un argument est appelé un constructeur de conversion.
11.2 Par un opérateur : opérateur de conversion.
sous la forme X :: operator T () => conversion du type X vers le type T.
Class BigInt {
char text[ 50]; public :
BigInt (int i ) ; // constructeur (de conversion)
void operator= ( int i ); // affectation
operator int () ; // operateur de conversion
} ;
BigInt big = 123; // construction : big = BigInt( 123 ) big.text "123"
big = 5; // affectation : operator = big.text "5"
int i ;
i = big; // conv. de type : i = big.operator int( ) i (entier( big.text )
Autre exemple :
Point p( 1, 2 ); Complex z;
Class Point {
int abscisse, ordonnee ; public :
Point( int a, int o ) { abscisse = a; ordonnee = o; }
operator Complex () { return Complex (abscisse, ordonnee ) ; } } ;
Complex {
float re, im ; public :
Complex( float xx = 0, float yy = 0 ) ; Complex operator + ( Complex ) } } ;
Autre exemple : soit une classe String On peut y définir un opérator const char *
cet opérator sera utilisé chaque fois qu’une fonction a besoin d’un const char * en paramètre et qu’on lui passera un objet du type String
class String {
char *str; public :
String( const char * ) ;
operator const char * () { return str ; } }
String s( "chaîne de caractère" );
12. Les modèles (templates)
Un modèle est un canevas décrivant une famille de composants : famille de fonctions, famille de type (class) ...
Les modèles de classes sont abondamment utilisés dans les librairies de classes du compilateur ou dans les classes vendues dans le commerce.
12.1 Déclarations et définitions.
12.1.1 Modèles de fonctionsDéfinition d’une fonction générique paramètrée par une constante
template <int N> // définition d'un modèle de fonction void f ( )
{
float tab[ N ] ;
for ( int i = 0 ; i < N ; i++ ) cin >> tab[ i ] ; // ...
}
la fonction f est un modèle de fonction qui utilise un tableau de float. La taille du tableau sera défini plus tard par le "paramètre" N
Jusqu'ici le compilateur connaît la définition d'un modèle de fonction mais aucune fonction n'est implémentée ( générée ).
f< 10 >() ; // création et appel d'une fonction avec N = 10 f< 50 >() ; // création et appel d'une autre fonction avec N = 50
ici deux fonctions f sont implémentées (générées et exécutables).
Les créations des fonctions sont faites à la compilation en fonction des appels.
12.1.2 Modèles de classe
Définition d’une classe générique "paramètrée" par deux constantes
template <int col, int lig> class Ecran
{
int colonne, ligne ; char ecran[ col ][ lig ]; public :
Ecran(); void efface() ; ...
Ecran < 25, 80 > ecranStandard; // création du type Ecran<25,80> // et création d'une variable de ce type
Ecran < 4, 40 > ecranLCD; // création d'un autre type Ecran<4,40> // et création d'une variable de ce type
Les fonctions membres définies à l'extérieur de la classe doivent être précédées de la définition des paramètres
template <int col, int lig>
void Ecran < col, lig > :: efface( ) {
for( int c = 0; c < col; c ++ )
for( int l = 0; l < lig; l ++ ) ecran[ c ][ l ] = ' '; }
12.2 Paramètres de modèle.
12.2.1 Les paramètres constants.Les paramètres sont des entiers, énumérations, pointeurs ou référence, mais pas des réels.
Template <int I > f( ) ; // ok
Template <float X> f( ) ; // erreur Template <float X> class C { } ; // erreur 12.2.2 les paramètres types.
template <class T> // pas une classe mais un type !
T min( T a, T b ) // définition d'un modèle de fonction
{ return a < b ? a : b ; } int main() { int i, j; char c, d; float x, y; ... i = min<int> ( j, 0 );
// création et appel de la fonction int min( int, int) c = min<char> ( d, ‘A’ );
// création et appel de la fonction char min( char, char) x = min<float> ( y, 3.14 );
// création et appel de la fonction float min ( float, float) return 0;
}
on trouve aussi le mot-clé typename à la place de class
template <typename T> class A { } ;
Les arguments du modèle doivent apparaître obligatoirement dans la liste des paramètres de l'appel de la fonction
template <class A, class B, class C>
C min ( A a, B b ) // erreur : C n’est pas un paramètre de min () {
C c = a < b ? a : b ; return c;
}
12.2.3 Déduction des paramètres types.
Le compilateur peut déduire parfois tout seul le type des "paramètres" du modèle au moment de l'instanciation. L'appel peut alors être simplifié.
// avec la fonction modèle précédente : template <class T> T min( T a, T b )
float x, y ;
x = min( x, y ); // le compilateur en déduit x = min <float> ( x, y )
mais
float x, y ;
x = min( y, 3 ); // erreur, pas de modèle pour ... min ( float, int )
Aucune correspondance ne convient, aucune conversion (cast) n’est appliquée et il y a erreur de compilation
12.2.4 Paramètres par défaut.
template <class T = int, int taille = 100> class Tableau
{
T tab[ taille ]; void erreur() ; public :
T & operator [ ] (int i ) { assert( i < taille ) ; return tab[ i ] ; }
const T & operator [ ] (int i ) const // pour un tableau constant { assert( i < taille ) ; return tab[ i ] ; }
} ;
Tableau < float, 20 > tabReel ;
Tableau < Complex > tabComplex; // Tableau < Complex, 100 > Tableau <> tabEntier; // Tableau < int, 100 >
12.3 Spécialisation partielle d'un modèle.
Il est possible (et parfois obligatoire) de redéfinir une fonction déjà définie par un modèle
template <class T> // modèle générique
T min( T a, T b ) {
return a < b ? a : b ; }
template<>
char *min( char *a, char *b ) {
return strcmp( a, b ) < 0 ? a : b ; }
12.4 Autres exemples.
Classe tableau de tout type
template <class Type> class Tableau
{
Type *tab; int taille ; public :
Tableau( int n ) : taille( n ), tab( new Type[ taille ] ) { } ~Tableau() { delete [] tab };
... } ;
Tableau <int> tabEntier( 5 );
Tableau <Complex> tabComplex( 10 ); Tableau <String> tabString( 100 );
Autre exemple : création d'un modèle de gestion de liste d’élements
Lors de la création de la classe liste, on ne connait pas le type des éléments
template <class Type> // Type sera défini à la demande
class Element {
Type info;
Element *suivant;
friend class Liste <Type> ; // un objet Liste doit connaitre Element public :
... } ;
template <class Type> class Liste
{
Element <Type> * premier; Element <Type> * courant ; Element <Type> * dernier; public :
Listee() ; ~Liste();
Type tete() { return premier->info ; } void ajoute( const Type& );
... }
Liste <int> listeEntier; Liste <Point> listePoint;
13. La gestion des exceptions.
13.1 Introduction.
La gestion d'erreur peut se faire de différentes manières.
Méthode brutale: l'utilisation de assert( ) : interruption forcée du programme.
FILE * f = fopen( "fichier", "w" ) ; assert ( f != NULL ) ;
Utilisation d'une variable globale (exemple errno des bibliothèques standards du C) et gestion de cette variable globale par le programmeur.
Code de retour pour chaque fonction et propagation de ce code de retour jusqu'à l'appelant qui voudra bien le traiter.
bool f( )
{ if( ... ) return true else return false ; }
Comment se mettre d'accord entre un concepteur de composant et son utilisateur pour traiter les erreurs ?
13.1.1 Principe :
Un mécanisme de gestion des erreurs a été introduit dans le langage lui-même par la notion d'exception.
On nomme exception une rupture de la séquence du programme.
Cette rupture est décidée en général à cause d'un problème grave : division par 0, indice de tableau trop grand ou négatif, mémoire insuffisante, pointeur nul, dépassement de calcul ... Lorsqu'une fonction veut signaler une erreur, elle lance une exception (en général un objet erreur).
L'utilisateur qui veut gérer l'erreur, attrape l'objet lancé et exécute le traitement adéquat. Si l'erreur n'est pas attrapée, un traitement par défaut termine le programme.
Structure de controle
throw / try / catch
13.2 exemple :
class Pile {
int *pile, sommet, taille ; public :
Pile( int taille ); void empile( int i ) ; int depile( ) ;
Pile :: Pile( int taille ) {
if( taille < 0 ) throw( taille ); this->taille = taille ;
pile = new int[ taille ] ; sommet = 0 ;
}
void Pile :: empile( int i ) {
if( sommet >= taille ) throw( "pile pleine" ); pile[ sommet++ ] = i ;
}
int Pile :: depile( ) {
if( sommet == 0 ) throw( "pile vide" ); return pile[ --sommet ];
} int main( ) { try { Pile pile( 5 ); pile.empile( 1 ) ; pile.empile( 2 ) ; cout << pile.depile() ; cout << pile.depile() ; cout << pile.depile() ; } catch( char *msg ) { cout << endl ; cout << msg ; exit( - 1 ) ; } catch( int i ) { cout << endl ;
cout << "erreur à la crétion de la pile, taille incorrecte: " << i ; exit( - 2 ) ;
}
return 0 ; }
13.3 l'instruction throw().
Dès qu'une erreur apparait, le programmeur peut lancer ou lever une exception par l'instruction throw() .
Le paramètre du throw est une variable.
Ce sera en général un objet d'une classe d'exception.
On sort immédiatement de la fonction avec l'instruction throw et on ne revient plus dans la fonction. Il y a ensuite en général interruption du programme.
int Pile :: depile( ) {
if( sommet == 0 ) throw( "pile vide" ); return pile[ --sommet ];
}
13.4 Le bloc try.
Les instructions à surveiller sont mises dans un bloc précédé du mot clé try . Le bloc try contient les instructions susceptibles de lancer une exception.
try {
// appels de fonctions susceptibles de lancer des exceptions // directement ou indirectement
}
Si une exception est lancée (directement ou indirectement) par une des instructions du bloc try , cette exception est attrapée par le bloc et aussitôt le programme se déroute du bloc try et rentre au début du bloc catch qui doit gérer l'exception.
13.5 Le bloc catch.
try { // instructions } catch ( TypeException_1 ex ) { // // instructions }catch (TypeException_2 ex) { // instructions
}
Le bloc catch doit suivre immédiatement le bloc try. Le bloc catch contient les gestionnaires d'exceptions nécessaires.
Un bloc catch commence par le mot clé catch suivi entre parenthèses comme une fonction d'un paramètre du type d'une classe d'exception, puis du bloc d'instructions à exécuter.
Le bloc catch exécute ses instructions et en général se termine par l'instruction exit() ou abort(). Mais il peut, avant d'interrompre le programme donner à l'utilisateur des
indications sur l'erreur, et faire effectuer des sauvegardes nécessaires.
S'il n'y a pas d'arrêt du programme dans un bloc catch, le programme reprend après le bloc try / catch qui a saisi l'exception.
Sortie immédiate de la fonction
Le bloc catch exécuté est le premier bloc rencontré avec le type d'exception correspondant à la variable d'exception lancée.
Si aucune classe d'exception des blocs catch ne correspond à l'exception lancée par throw, la recherche se poursuit dans les blocs try / catch englobant.
Si on ne trouve aucun gestionnaire de la même classe, le programme appelle la fonction
teminate().
13.5.1 Choix du bon gestionnaire d'exception.
catch( Truc t) // gestionnaire de l'exception truc ou d'une classe derivée { // }
catch( ... ) // gestionnaire de n'importe quelle exception { // }
catch( Chose * c )// gestionnaire de l'exception chose ou d'une classe dérivée
{ // }
ici le gestionnaire de chose ne pourra jamais être exécuté !
13.5.2 Prévision des exceptions possibles.
Une fonction (même la fonction main() ) peut prévoir (spécifier) les exceptions susceptibles de se produire.
int f () throws( TableauLimite, TableauCreation ) { ... }
les exceptions prévues (spécifiés) seront lancées (throw). Toute autre exception non prévue qui serait levée à l'intérieur de la fonction entraîne l'appel de la fonction particulière
Pile :: Pile( int taille ) {
if( taille < 0 ) throw( taille ); this->taille = taille ;
pile = new int[ taille ] ; sommet = 0 ;
}
void Pile :: empile( int i ) {
if( sommet >= taille ) throw( "pile pleine" ); pile[ sommet++ ] = i ;
}
int Pile :: depile( ) {
if( sommet == 0 ) throw( "pile vide" ); return pile[ --sommet ];
}
void initialisePile( Pile &p ) {
for( int i = 0; i < 10; i++ ) p.empile( 2*i ); } int main( ) { try { Pile pile( 5 ); initialisePile( pile ) ; pile.empile( 1 ) ; cout << pile.depile() ; } catch( char *msg ) { cout << endl ; cout << msg ; exit( - 1 ) ; } catch( int i ) { cout << endl ;
cout << "erreur à la création de la pile, taille incorrecte: " << i ; exit( - 2 ) ; } return 0 ; } 2 1 3 sans exit()
13.6 Autres exemples
13.6.1 Exemple 1// déclaration des classes d'exception Class TableauCreation
{ public :
int indice;
TableauCreation( int n ) { indice = n ; } // constructeur };
Class TableauLimite {
public : int indice;
TableauLimite( int n ) { indice = n ; } // constructeur };
class Tableau // classe Tableau
{ int taille; int *tab ; public : Tableau( int ); ~Tableau();
int &operator [] (int ); // surdéfinition de l'opérateur [] } ;
Tableau :: Tableau (int i ) // constructeur {
if ( i <= 0 ) {
TableauCreation erreur( i ); // création d'un objet d'exception throw( erreur ); // lancement de l'exception }
tab = new int[ taille = i ] ; }
int &Tableau :: operator [] (int i ) // opérateur [] {
if ( i < 0 || i >= taille ) {
TableauLimite erreur( i ); // création d'un objet d'exception throw( erreur ); // lancement de l'exception }
return tab[ i ]; // ok
int main() // essai {
try {
Tableau t( -3 ) ; // provoque l'exception TableauCreation t[ 20 ] = 2; // provoque l'exception TableauLimite }
catch( TableauCreation e ) {
cout << "exception de création avec indice : " << e.indice << endl; exit( 1 );
}
catch( TableauLimite e ) {
cout << "exception d'indice avec indice : " << e.indice << endl; exit( 1 ); } } 13.6.2 Exemple 2 f1() { try{ doSomething f2() ; doSomething } catch( … ) { doSomething exit( 1 ); } catch( … ) { doSomething exit( 1 ); } } terminate() f2() { try{ doSomething f3() ; doSomething } catch( … ) { doSomething exit( 1 ); } } f3() { doSomething if( …) throw( …); doSomething } Figure 5 Ou Ou
14. Les entrées/sorties (Streams ou flots).
14.1 Rappels
Les streams sont déclarés dans le fichier entête iostream.h
14.2 La classe ostream : classe des flots de sortie.
Elle surdéfinit l’opérateur << (insertion) sous la forme d’une fonction membre pour écrire toute valeur de type prédéfini du langage :
cout << char, entier, long, réel, char *, adresse, …
L’opérateur << renvoie une référence sur le flot, ce qui permet de cumuler les sorties.
int i = 3; char c = ‘a’;
char s[] = " bonjour ";
cout << "i =" << i << "c=" << c << "s=" << s << ‘\n’ ;
14.2.1 Redéfinition de l'opérateur <<
Pour écrire dans un flot un objet de type T avec l’opérateur <<, il suffit de définir une
fonction amie dans la classe T correspondant à l’une des formes suivantes : ostream & operator << ( ostream &, T )
ostream & operator << ( ostream &, T& )
ostream & operator << ( ostream &, const T& )
exemple :
ostream & operator << ( ostream &o, const Complex &c ) {
return o << c.x << ‘+’ << c.y << ‘i’ ; }
Complex z( 2, 3 );
cout << z ; // écrit 2+3i
14.2.2 Autres fonctions de la classe ostream :
ostream &write( char *texte, int n ) Ecrit le tableau jusqu'à la taille n.
cout.write( "maison" , 5 ) ; // écrit : maiso
cout.put( '\n' ) ; // effectue un saut de ligne
14.3
La classe istream : classe des flots d’entrée.
1. Elle surdéfinit l’opérateur >> (extraction) sous la forme d’une fonction membre pour lire toute valeur de type prédéfini du langage.
L’opérateur >> renvoie une référence sur le flot, ce qui permet de cumuler les entrées.
int i ; char c ;
cin >> i >> c ;
Pour lire un objet d’un nouveau type T dans un flot d’entrée avec l’opérateur >>, il suffit de définir une fonction amie :
istream & operator >> ( istream &, T& )
istream & operator >> ( istream &i, Complex &c ) {
char carac;
i >> c.x >> carac; assert( carac == ‘+’ ); i >> c.y >> carac; assert( carac == ‘i’ ); return i;
}
Complex z ;
cin >> z ; // il faut taper par exemple 2 + 3i
14.3.1 Fonctionnement de l'opérateur d'extraction >> Saisie d'un tableau de char :
"saute" les blancs au début
arrête la saisie dès le premier séparateur trouvé,
les autres caractères restent dans le flux, dont le séparateur.
ATTENTION : n'arrête pas la saisie si le tableau de saisie est plein !
char texte [ 50 ] ; cin >> texte ;
// si on frappe " saisie de mot " ( = Entrée) // alors texte contient "saisie"
// et il reste " de mot " dans le flux d'entrée.
saisie d'un entier :
"saute" les blancs au début
arrête la saisie dès le premier caractère différent d'un chiffre
int i ; cin >> i ;
// si on frappe " 123A" ou " 123 " // alors i contient 123
// et il reste "A" ou le caractère "" (Entrée seul) saisie d'un réel :
"saute" les blancs au début
arrête la saisie dès le premier caractère différent d'un chiffre ou d'un point
double d ; cin >> d ;
// si on frappe " 3.14A" ou " 3.14 " // alors d contient 3.14
// et il reste "A" ou le caractère "" (Entrée seul) En conclusion cin >>
ne permet pas de saisir des espaces
Le séparateur de fin de saisie reste dans le flux
Il est bon de vider le flux avant ou après une saisie (avec get() ou getline() ) Le fonctionnement est similaire avec scanf().
14.3.2 Autres fonctions de la classe istream :
La fonction get() : plusieurs formes
int get();
rend le premier caractère du flux sous la forme d'un entier, les autres restent dans le flux.
istream& get(char*, int len, char terminal= '\n');
prend les caractères dans le flux s'arrête
quand la longueur len – 1 est atteinte et ajoute '\0' dans la saisie ou quand on rencontre le caractère terminal.
Le caractère terminal reste dans le flux et ajoute '\0' dans la saisie ne saute pas les "blancs" au début
la saisie contient toujours à la fin le caractère '\0' les autres caractères restent dans le flux
istream& get(char&);
rend le premier caractère du flux ctrl Z rend 0.
istream& get(streambuf&, char = '\n');
La fonction getline()
istream& getline(char*, int len, char terminal= '\n');
même comportement que get( char *, int, int ) mais
Lit donc un caractère de plus que get( char *, int, int ).
En conclusion : get( char *, int, int ) et getline( char *, int, int ) Saisissent les espaces (par opposition à cin >> )
Utiliser cin.get( char *, int, int ) puis cin.get() pour retirer le séparateur ou cin.getline(char *, int, int) qui lit aussi le séparateur.
getline( … ) est plus interessant que get( … )
Fonctionnement un peu similaire à gets() mais dans gets : pas de contrôle de la taille du buffer, le caractère terminal est remplacé par '\0' (comme getline() ).
La fonction gcount()
int gcount ( );
Renvoie le nombre de caractères saisis par get et getline (et read)
(avec getline on lit un caractère de plus car le caractère terminal est retiré du flux)
char texte[ 50 ] ; int i ;
i = cin.get() ;
// si on frappe "56"
// alors i contient 0x35 et il reste "6"
cin.get( texte, 5) ; i = cin.gcount() // si on frappe "123"
// alors texte contient "123" i contient 3 et il reste "" cin.get( texte, 5) ; i = cin.gcount()
// si on frappe "12345678"
// alors texte contient "1234" i contient 4 et il reste "5678" cin.getline( texte, 5) ; i = cin.gcount()
// si on frappe "123"
// alors texte contient "123" i contient 3 et il ne reste rien cin.getline( texte, 5) ; i = cin.gcount()
// si on frappe "12345678"
// alors texte contient "1234" et i contient 4 et il reste "5678" La fonction read()
istream& read(char*, int len);
même comportement que get( char *, int, int ) mais n'utilise pas de caractère terminal.
Peu intéressant.
La fonction peek()
int peek( );
Rend le caractère dans le flux sans l'extraire. Les fonctions tellg() et seekg()
14.4
Le statut d’erreur d’un flot
Ensemble de bits d’un entier :
les bits d’erreur sont définis comme constante dans la classe ios. ios::eofbit
ios::failbit ios::badbit ios::goodbit.
Pour lire tout le statut : int rdstate().
pour modifier le statut : void clear( int = 0)
fl.clear( ios::badbit | fl.rdstate() ) ; // active le bit ‘bad’
fonctions d'état : bool good() ; bool fail() ; bool eof() ;
14.5 Formatage.
Le formatage est effectuée à l’aide de manipulateurs Il faut inclure le fichier entête <iomanip.h> Les manipulateurs sont inclus dans le flot.
Quelques exemples : (les manipulateurs sont très nombreux).
dec : conversion en décimale hex : conversion en hexadécimale oct :
setbase( int )
fixed , scientific : notation fixe ou scientifique endl : insère un saut de ligne et vide le tampon
setw( int ) définit le gabarit. A utiliser avant chaque opération !
setprecision( int ) définit la précision des nombres flottants.
setfill( int ) définit le caractère de remplissage. A utiliser avant chaque opération !
int i = 16;
float x = 12.3456;
cout << ‘*’ << hex << i << ‘*‘ << setw( 8 ) << setprecision( 2 ) << x << ‘*’; // affiche *10* 12.35*
14.6 Les flux fstream : les fichiers (ofstream, ifstream)
14.6.1 Mode d'ouvertureios::out en sortie (écriture) (détruit le fichier existant) ios::app en ajout à la fin (conserve le fichier existant) ios::trunc efface le contenu à l'ouverture ????
ios::binary en mode binaire (utiliser read et write)
14.6.2 Constructeur
fstream( const char *name, int mode, int prot = …. ); ifstream( const char *name, int mode = ios::in );
ofstream( const char *name, int mode = ios::out | ios::trunc );
14.6.3 Autres membres
void ifstream::open( const char *name, int mode = ios::in );
void ofstream::open( const char *name, int mode = ios::in | ios::trunc ); bool fstream::is_open() ;
void fstream::close() ; void fstream::eof() ;
14.6.4 En écriture on peut utiliser
<< avec tout type de base put( char )
write( char *, int n) // écrit n caractères, ne s'arrête pas en '\n' ou '\0' ! flush()
14.6.5 En lecture on peut utiliser
>> avec tout type de base get ( )
get ( char *, int ) getline( char *, int ) long tellg()
seekg( pos )
count() pour connaître le nombre de caractères lu
14.6.6 exemple :
#include <fstream.h> char ligne[ 80 + 1] ;
char nomFichier[ ] = " …….." ;
ifstream fEntree;
fEntree.open( nomFichier, ios::in ); if( !fEntree.good() ) { cerr << "erreur ….. " ; return ; } while( … ) { fEntree.getline( ligne, 80 + 1, '\n' ); if( fEntree.eof() || fEntree.fail() ) break ; int taille = fEntree.gcount( ) – 1 ;
…… }
fEntree.close() ;
14.7 Les flux strstream : les chaînes de caractères
14.7.1 Mode d'ouvertureios::in en entrée (lecture)
ios::out en sortie (écriture) (détruit la chaîne existante) ios::app en ajout à la fin (conserve la chaîne existante) ios::trunc efface le contenu à l'ouverture ????
ios::binary en mode binaire (utiliser read et write)
14.7.2 Constructeur
strstream( ) ; utilise un buffer alloué dynamiquement
strstream( signed char *buf, int sz, int mode ); utilise le buffer proposé
14.7.3 Autres membres
char *str() ; retourne le buffer et le gèle istrstream &getline( char *, int, int )
14.7.4 En écriture on peut utiliser
<< avec tout type de base put( char )
write( char *, int ) flush()
14.7.5 En lecture on peut utiliser
>> avec tout type de base get ( )
get ( char *, int ) getline( char *, int ) long tellg()
seekg( pos )
count() pour connaître le nombre de caractères lu
strstream chaîne ; int i = 5 ;
double d = 3.14 ;
chaine << " i = " << i << " d = " << d ; // équivalent à sprintf() chaine << " fin "