Vous êtes sur la page 1sur 47

I U T S T I D P a u A l b e r t R O Y E R

I.U.T. DES PAYS DE L’ADOUR

Statistique et Traitement Informatique des Données

Éléments du langage C++

par Albert ROYER


AIDE-M É MOIRE

Éléments du langage C++

Les informations contenues dans ce


document sont destinées aux étudiants de
deuxième année.
Toute remarque sur son contenu sera la
bienvenue.

© 1998 A. ROYER IUT STID, Pau. Tous droits réservés.


© 2000 Révision A. ROYER IUT STID, Pau.
Table des matières

0.

AVANT-PROPOS 1
Première version : fract1.cpp 1
Structure du texte 3
Commentaire 3
include 3
Déclarations de types 3
Déclarations de fonctions 3
Définition du programme principal3
Contenu des fonctions 3

Premiers enseignements 4
Les opérateurs 4
arithmétiques 4
de comparaison 4
Les entrées-sorties de base 4

1. LES ASPECTS CLASSIQUES DU LANGAGE 1


Déclarations 1
Constantes 1
Types 1
Tableau 1
Structure 2
Énumération 2
Fonctions 2
Variables 3

Chaînes de caractères 3
strlen 4
strcpy 4
strcat 4
strcmp 5

Instructions 5
Séquentielles 5
Itératives 5
for 5
while 6
do 6
break 6
continue 7
Sélectives 7
Conditionnelle 7
Choix multiple 8
Autres instructions de contrôle 8
return 8
goto 9

Compléments sur les entrées/sorties de base 9


Clavier 9
Écran 9

2. ADRESSES ET POINTEURS1
Adresses 1

Pointeur 1
Déclaration 1
Opérateurs & et * 2
Opérateur d'adressage & 2
Opérateur d'indirection * 2
Passage de pointeurs en paramètres2

Pointeurs et autres types 2


Pointeurs et tableaux 2
Pointeurs et structures 3
Pointeurs et références 3

3. LES FLUX 1
Fichiers 1
Généralités 1
Suite d’octets 1
fstream.h 1
Type d’utilisation de fichiers 1
Opérations communes à tous les fichiers 2
Ouverture du fichier 2
Fermeture du fichier 2
Contrôle de l’état d’un flux 2
Opérations de manipulation de fichiers 3
Fichier ofstream 3
Fichier ifstream 4
Fichier fstream 5
Accès direct 5
À un octet paticulier 5
À un enregistrement 5
Présentation des informations 5
Formatage 5
Manipulateurs 6
Pour en savoir plus 6
Autres modes d'ouverture 6
Définition des opérateurs d'entrée-sortie 7

4.LES OBJETS 1
I U T S T I D P a u A l b e r t R O Y E R

Classe 1
Membre de classe 1
Données membres 1
Fonctions membres 1
visibilité des membres 1
Déclaration de la classe fraction 2
Définitions des opérations 2
Utilisation de la classe fraction 2

Héritage 3
Dérivation 3
neofraction.h 3
neoprincipal.cpp 3
neofraction.cpp 3

Constructeur 4
fraction.h 4
programme principal 4
fraction.cpp 4

Amis 5
fraction.h 5
fraction.cpp 5
programme principal 6

Conclusion 6
Chapitre

0
A l b e r t R O Y E R I U T S T I D P a u

Avant-propos
Afin de parcourir les possibilités du langage C++, nous allons nous
appuyer sur un même programme dont l’objectif est de permettre la
manipulation de fractions.

Pour nous, une fraction, ou nombre rationnel, est la valeur du quotient


de deux entiers : le numérateur et le dénominateur (0 ).

Nous limitons l’écriture de chaque fraction à une seule que nous


appellerons normalisée ; c’est-à-dire fraction réduite avec le signe
accolé au numérateur.

N
otre programme devra autoriser la saisie de deux fractions et renvoyer le
résultat d’une des quatre opérations arithmétiques appliquées à ces deux
fractions.

Première version : fract1.cpp


Ce premier programme peut s’écrire ainsi :

#include <iostream.h>

// fract1.cpp, première version du programme implantant les fractions


// - créé par A. Royer le 23 juil. 97

// déclaration du type FRACTION


struct FRACTION {
int NUM; // entier signé
int DEN ; // entier positif
};

// déclaration des différentes fonctions du programme


int pgcd (int a, int b){
// en entrée : deux entiers
// valeur de retour : le pgcd des 2 nombres
int m, n;

// on ne s'intéresse qu'aux valeurs absolues des paramètres


if (a<0) m = -a; else m = a;
if (b<0) n = -b; else n = b;
if (m*n == 0)
// cas particulier où au moins un des deux nombres est nul
return m+n;

0-1
else // cas général
while (m!=n)
if (m>n) m = m - n;
else n = n - m;
return m;
}

void normaliser (FRACTION & F){


// en entrée et en sortie : la fraction objet de la normalisation
int D;

// traitement du signe éventuellement négatif du dénominateur


if (F.DEN<0) {
F.NUM = -F.NUM;
F.DEN = -F.DEN;
}

// réduction de la fraction
D = pgcd(F.NUM, F.DEN);
F.NUM = F.NUM / D;
F.DEN = F.DEN / D;
}

FRACTION saisir (void){


// valeur de retour : la fraction saisie
FRACTION F;

cout << "Numérateur ? "; cin >> F.NUM;


cout << "Dénominateur ? "; cin >> F.DEN;
return F;
}

void afficher (FRACTION F){


// en entrée : la fraction à afficher
cout << F.NUM << " / " << F.DEN;
}

FRACTION plus (FRACTION F1, FRACTION F2){


// en entrée : deux fractions
// valeur de retour : la fraction somme des deux précédentes
FRACTION F;

F.DEN = F1.DEN * F2.DEN;


F.NUM = F1.NUM * F2.DEN + F2.NUM * F1.DEN;
normaliser(F);
return F;
}

void main(){
FRACTION F1, F2, F3;

cout << "Entrer la première fraction \n";


F1 = saisir();
cout << "Entrer la deuxième fraction \n";
F2 = saisir();
// pour éviter d'allonger le texte, on ne programme que l'addition
F3 = plus(F1,F2);

0-2
A l b e r t R O Y E R I U T S T I D P a u

cout << "Le résultat est : ";


afficher(F3);
cout << endl;
}
Structure du texte
En C++, on trouve la référence au module définissant les entrées-sorties, les déclarations de constantes,
de types, des fonctions puis la définition de la fonction principale repérée par le mot-clé main.
Commentaire
Il commence avec // et s’arrête à la fin de la ligne. Un commentaire de plusieurs lignes se parenthèse
par /* et */.
include
On indique par une ou plusieurs lignes #include les éléments de bibliothèques que l’on va utiliser dans
le programme, ici les entrées-sorties.
Déclarations de types
On trouve en début de programme les déclarations utiles à l’ensemble du programme. Dans ce premier
exemple, nous déclarons un agrégat hétérogène (appelé structure par les C-istes) pour définir une
fraction.
Déclarations de fonctions
Il n’y a en C++ qu’une seule catégorie de sous-programme : les fonctions. Une fonction renvoie un objet
d’un certain type (Exemples : FRACTION pour saisir, entier pour pgcd) qu’elle calcule à partir de
paramètres de types quelconques (Exemples : f de type FRACTION pour normaliser , a et b de type
entier pour pgcd).

L’équivalent d’une procédure est réalisé par une fonction dont la valeur de retour n’existe pas ; en C++,
pour une procédure la valeur de retour est neutralisée en la déclarant de type void. Ce même mot-clé
void est aussi utilisé pour remplacer une liste de paramètres vide.

On préférera la version fract2.cpp qui ne donne que l’entête des fonctions avant le texte du programme
principal et qui définit in extenso les mêmes fonctions après (cf. annexe).
Définition du programme principal
Tout programme contient une et une seule fonction principale, appelée aussi fonction main ; c’est par
elle que commence l’exécution du programme. Cette fonction, comme les autres peut retourner une
valeur d’un type quelconque et peut disposer de paramètres. Classiquement, la valeur retournée par la
fonction main est prise en compte par le système d’exploitation qui a pu éventuellement lui fournir des
paramètres lors du lancement du programme.
Contenu des fonctions
Le contenu d’une fonction est encadrée d’accolades. On y trouve des déclarations et des instructions.

Une déclaration de variables commence par le nom du type et se poursuit par la liste des variables de ce
type.

On retrouve les instructions rencontrées dans les autres langages de programmation, avec une syntaxe
qui peut parfois surprendre. Dans cet exemple, il y a :

 l’instruction d’affectation dont le symbole est =,

0-3
 l’instruction conditionnelle if (condition) instruction_vraie [else instruction_fausse],

 une des instructions de bouclage while (condition) instruction ,

 l’appel de fonction, qui se note toujours avec des parenthèses (Exemple : saisir() dans le programme
principal),

 l’instruction return , qui précède l’expression de la valeur à renvoyer,

 les opérations d’entrée-sortie repérables par les opérateurs d’injection ( <<) et d’extraction (>>).

Premiers enseignements
Les opérateurs
arithmétiques
Les opérateurs arithmétiques sont : + - * / bien sûr. Il faut y ajouter % qui est l’opérateur modulo. On
notera qu’il n’y a pas d’opérateur d’élévation à la puissance.
de comparaison
Les opérateurs de relation sont : < <= > >=. L’opérateur de comparaison d’égalité est donné par ==
et celui d’inégalité par !=.

Les entrées-sorties de base


Les entrées effectuées via le clavier et les sorties réalisées grâce à l’écran se veulent simples. A priori cin
correspond au clavier et cout à l’écran.

Ainsi cin >> VARIABLE correspond à la saisie au clavier de la valeur qu’on veut attribuer à la
variable. De même, cout << EXPRESSION provoquera l’affichage de la valeur de l’expression. Si on
veut combiner dans une même instruction deux voire plusieurs échanges avec le périphérique il suffit de
respecter la syntaxe suivante :
cin >> VARIABLE [ >> VARIABLE ]

cout << EXPRESSION [ << EXPRESSION ]

Remarques

 Dans le premier exemple l’utilisation pour cout du manipulateur endl permet de provoquer un
saut de ligne.

 Lors de la saisie (cin), les espaces (ainsi que les tabulations et les passages à la ligne) sont
considérées comme des séparateurs entre les données.

 '\n' dénote le caractère de fin de ligne ; de manière générale, \ confère au caractère qui suit
une signification particulière.

0-4
A l b e r t R O Y E R I U T S T I D P a u

Chapitre

1
A l b e r t R O Y E R I U T S T I D P a u

Les aspects classiques du langage


Sans être exhaustif, on veut donner ici les principaux éléments du
langage qu’on trouve habituellement dans tout programme.

N
ous adoptons ici une décomposition classique des possibilités d’un langage en
déclarations et instructions. Nous y ajoutons des compléments sur les entrées-
sorties.

Déclarations
À partir des types de base (char, float, int, ...), on peut déclarer des constantes, des types, des
variables et des fonctions.

Constantes
Syntaxe
const TYPE IDENTIFICATEUR = EXPRESSION

Exemples

1. const char * LANGAGE = C++ ;

2. const float PI = 3.14159 ;

3. const int DIX = 10 ;

Types
Les constructeurs de types permettent de définir de nouveaux types
par regroupement de plusieurs valeurs

de même type, ce sont les tableaux (agrégats homogènes),

de type différents, ce sont les structures (agrégats hétérogènes),

en énumérant toutes les valeurs possibles, ce sont les énumérations.


Tableau
Syntaxe
typedef IDENTIFICATEUR [ EXPRESSION ] [ [ EXPRESSION ] ]

Exemples
1. typedef char ALPHABET [26] ;

2. typedef float MATRICE [DIX] [2*DIX] ;

1-1
3. typedef int OCCURRENCES [256] ;

On remarque l’utilisation des crochets qui permet de suite de savoir qu’il ne s’agit pas de fonction.
Structure
Syntaxe
struct IDENTIFICATEUR { TYPE VARIABLE ; [ TYPE VARIABLE ; ] } ;

Exemples
1. struct SAUT {
int essai_no ;
float hauteur ;
}
2. struct ADRESSE {
int numero ;
char rue [20] ;
int code_postal ;
char ville [25] ;
}
3. struct PERCHISTE {
char nom [20] ;
SAUT performance;
}
La notation pointée communément utilisée ailleurs est également de mise en C++.
Énumération
Syntaxe
enum IDENTIFICATEUR { IDENTIFICATEUR [, IDENTIFICATEUR ] }

Exemples
1. enum BOOLEEN { FAUX, VRAI } ; // FAUX est codé 0, VRAI 1

2. enum MOIS { JAN=1, FEV, MAR, AVR, MAI, JUIN, JUIL, AOU, SEP, OCT,
NOV, DEC } ; //les mois sont codés à l'aide de leur numéro usuel

La première valeur utilisée par la codification peut être changée.

Fonctions
Il n’y a pas possibilité de déclarer des fonctions à l’intérieur d’autres fonctions.

Syntaxe

pour la fonction
TYPE IDENTIFICATEUR ( LISTE DE PARAMETRES ) {...}

1-2
A l b e r t R O Y E R I U T S T I D P a u

pour la liste de paramètre


TYPE [&]IDENTIFICATEUR [,TYPE [&] IDENTIFICATEUR ]

Exemples
1. int pgcd (int a, int b) {... return d ;}

2. void normaliser (FRACTION & f) {...}

3. void autre (const PARAMETRE p) { … }

Il y a donc deux modes de passages de paramètres : par valeur et par référence. Le passage par valeur,
utilisé pour la fonction pgcd, implique que les paramètres effectifs ne subissent aucune influence de la
part de la fonction pgcd. Le passage par référence (précisé par l'utilisation de &), utilisé dans la fonction
normaliser , sous-entend que la fonction a un impact sur le paramètre effectif. Un paramètre constant
(précédé de const) ne peut pas être modifié à l'intérieur de la fonction.
Remarque : les tableaux sont, de fait, passés par référence, il n'y a pas utilisation de &.

Variables
On peut déclarer des variables à tout endroit du programme.

Syntaxe
TYPE IDENTIFICATEUR

Exemples
1. char caractere; // variable simple

2. MATRICE m; // variable tableau

3. BOOLEEN logique; // variable de type énuméré

4. PERCHISTE concurrent [10]; // tableau de structures

Comme ailleurs, toutes ces possibilités se combinent à souhait.

Chaînes de caractères
Il n'existe pas, comme dans d’autres langages, de type chaîne de caractères. Les chaînes de caractères
sont des tableaux de caractères dont le dernier élément utile est un caractère spécial, appelé terminateur
représenté par \0.

1-3
Exemple

La chaîne C++ sera stockée dans un tableau de la façon suivante :

C + + \0 … …

0 1 2 3 … …

Il n'existe pas d'opérateurs sur les chaînes de caractères. La manipulation des chaînes de caractères se fait
en utilisant les fonctions de bibliothèque décrites dans string.h parmi lesquelles on peut citer :

strlen
Rôle

Renvoie la longueur de la chaîne de caractères passée en paramètre (le terminateur ne


compte pas).

Exemple
lg = strlen (chaine);

// si chaine est le tableau schématisé ci-dessus, l'entier lg vaudra 3.

strcpy
Rôle

Copie le deuxième paramètre dans le premier ; seul moyen d'affecter des chaînes entre elles.

Exemple
strcpy (cible, source);

// réalise l'affectation cible  source

strcat
Rôle

Concatène le deuxième paramètre au premier.

Exemple
strcat (cible, source);

/* si cible="bon" et source="jour" , après l'appel de strcat, cible vaudra


"bonjour" */

strcmp
Rôle

1-4
A l b e r t R O Y E R I U T S T I D P a u

Compare les deux paramètres ; cette fonction renvoie une valeur entière négative si la
première chaîne vient avant la deuxième chaîne dans le dictionnaire, nulle si les deux chaînes
sont identiques ou positive si la première chaîne vient après la deuxième chaîne dans le
dictionnaire.

Exemple
if (strcmp (ch1, ch2) == 0)

// si ch1 est égal à ch2 …

Instructions
Les instructions du langage sont nombreuses et peuvent prendre diverses formes. Nous ne donnerons,
pour chaque instruction, que la forme qui nous semble la plus lisible.

Séquentielles
L'instruction vide n'a pas de nom particulier. Il y a instruction vide lorsqu'un point-virgule ( ;) ou une
accolade fermante d'un bloc (cf. instruction composée ci-dessous) est immédiatement suivi d'un autre
point-virgule.

Autre instruction séquentielle, l’affectation a, rappelons-le, pour symbole =. Remarquons qu'il existe,
parmi de nombreuses possibilités, deux écritures intéressantes combinant affectation et calcul : v++ qui
équivaut à v = v + 1 et v- — qui équivaut à v = v - 1.

L’instruction composée est réalisée en enfermant dans un bloc une ou plusieurs instructions. Le bloc est
délimité par des accolades. On remarque donc que le corps d’une fonction se réduit à un bloc.

Itératives
Il y a trois instructions de bouclage et deux instructions utiles aux boucles :
for
Syntaxe
for (EXPRESSION1 ; EXPRESSION2 ; EXPRESSION3) INSTRUCTION

Explication

EXPRESSION1 est calculée à l’entrée de la boucle.


EXPRESSION2 est calculée et si le résultat est faux (c'est à dire si la valeur est 0) la boucle
est arrêtée, sinon elle se poursuit.
EXPRESSION3 est calculée à chaque fin de boucle.

1-5
Exemples
1. for (int i = 1 ; i<10 ; i++) s = s + i ;

2. for (int j = N ; j>0 ; j--) p = p * j;

3. for (float f = 0.0 ; f<PI/2. ; f = f+0.1) CalculSinus(f) ;

D’autres formes existent, même si elles ne sont pas souhaitables ; ainsi for ( ; ; ) ...
while
Syntaxe
while ( CONDITION ) INSTRUCTION

Explication

Tant que la condition est vraie (c’est-à-dire l’expression 0), l’instruction est répétée.
Exemples
1. while (i<10) { s = s + i ; i++ ;}

2. while (j>0) { p = p * j; j-- ; } ou while (j--) p = p * j ;


3. while (f<PI/2.) { CalculSinus(f) ; f = f+0.1 ; }

do
Syntaxe
do INSTRUCTION while ( CONDITION )

Explication

On exécute une fois l’instruction et on la répète tant que la condition est vraie (c’est-à-dire
l’expression 0).
Exemples
1. do { s = s + i ; i++ ;} while (i<10) ;

2. do { p = p * j; j-- ; } while (j>0) ;

3. do { CalculSinus(f) ; f = f+0.1 ; } while (f<PI/2.) ;

break
Explication

L’instruction break provoque une sortie immédiate de la boucle en cours. En complément


de while, elle permet de réaliser le schéma unique de toute boucle :
faire INSTRUCTION ; sortir sur CONDITION ; INSTRUCTION ; fait ;
Syntaxe

1-6
A l b e r t R O Y E R I U T S T I D P a u

while (1) { INSTRUCTION ; if ( CONDITION ) break; INSTRUCTION ;


}

Exemples
1. while(1) { s = s + i ; if (i==9) break; i++ ;}

2. while(1) { p = p * j; if (j == 1) break; j-- ; }

3. while(1) { CalculSinus(f); if (f<(PI-.2)/2.) break; f = f+0.1 ; }

continue
Explication

L’instruction continue provoque une nouvelle itération en donnant le contrôle à la fin de la


boucle sans en sortir. Elle peut rendre service lors d’un traitement systématique sur des
données si celui-ci ne s’applique pas sur certaines d’entre elles.
faire INSTRUCTION ; si CONDITION reprendre; INSTRUCTION ; fait ;
Exemples

1. for (int i = 1 ; i<100 ; i++) { if (i==13) continue ; TRAITEMT ; }// effectue le


traitement symbolisé par TRAITEMT pour toutes les // valeurs de 1 à 100 sauf 13

2. while(j>0) { j-- ; if (j == 13) continue; TRAITEMT ;}

3. do { k++ ; if (k == 13) continue ; TRAITEMT ;} while (k<100) ;

Sélectives
Conditionnelle
Syntaxe
if ( CONDITION ) INSTRUCTION ; [ else INSTRUCTION ]

Explication

Si la condition est vraie (i.e. l’expression 0), la première instruction est effectuée, sinon
c’est l’instruction située derrière else qui l’est. La variante else n’est pas obligatoire.
Exemples

1. if (nombre<0) nombre = -nombre ; // Après cela, nombre est  0

2. if (a>b) b = b-a ; else a = a-b ; // cf. pgcd

Expression conditionnelle

Une condition est une expression dont la valeur 0 est assimilée à faux et les autres valeurs à vrai. Elle
peut s’écrire à partir de relations combinées par les opérateurs &&, || ou !.

Exemples de conditions
1. (x==3 && x==b)

1-7
2. ((n % 2) != 0) qui peut également s’écrire ( ! (n%2 == 0))
3. ( ((‘A’<=c)&& (c<=’Z’)) || ((‘a’<=c) && (c<=’z’)) )

Choix multiple
Syntaxe
switch ( EXPRESSION ) {
[ case CONSTANTE : INSTRUCTION ; ]
[ default : instruction ; ]
}

Explication

Une fois la valeur entière de l’expression évaluée, les instructions qui suivent la valeur sont
exécutées. Si aucune constante ne correspond à cette valeur, ce sont les instructions qui
suivent default qui sont exécutées.
On comprendra qu’il faut terminer le traitement associé à chaque case par l’instruction
break qui permet de sortir de l’instruction switch.
Exemples
switch (N) {
case 0 : cout << "zéro" ;
break ;
case 1 : cout << "un" ;
break ;
...
case 8 : cout << "huit" ;
break ;
case 19: cout << "dix-";
case 9 : cout << "neuf" ;
break ;
default : cout << "je ne sais pas" ;
}

Autres instructions de contrôle


return
Syntaxe

L’instruction return peut prendre trois formes syntaxiques :


1. return
2. return constante_entière
3. return (expression)

1-8
A l b e r t R O Y E R I U T S T I D P a u

Explication

La fonction en cours s’arrête, elle retourne à la fonction appelante la valeur de retour (cas 2
et cas 3). Dans tous les cas, la fonction appelante reprend.
Exemples

1. return ; // cas d’une fonction sans résultat

2. return 5 ; // pour les fonctions

3. return (0<=car && car<=9); // pour isdigit(car)

goto
Il existe aussi un goto en C++ dont nous ne parlerons pas...

Compléments sur les entrées/sorties de base


Dans le fichier spécial <iostream .h> sont définies toutes les fonctions permettant la saisie au clavier
et l'affichage à l'écran en plus des opérateurs d'injection et d'extraction.

Clavier
L'opérateur d'injection ne permet ni de saisir une espace, ni de saisir une chaîne de caractères (dans un
tableau) avec sécurité. C'est pourquoi nous retenons des multiples fonctions disponibles les suivantes :
get, getline et ignore.

Exemples

 cin.get (c) prend le prochain caractère tapé au clavier et le range dans c.

 cin.getline(ch, n) prend les n-1 prochains caractères et ajoute un nième


caractère de fin de chaîne (\0). Si on tape plus de caractères, ceux-ci ne seront pris
en compte qu’à la prochaine saisie.

 cin.ignore(n, car) saute les n caractères suivants ou saute les caractères


suivants jusqu'au caractère car. Ainsi, cin.ignore(256, '\n') saute les
caractères jusqu'à la fin de la ligne.

Écran
L'opérateur d'extraction suffit pour l'essentiel. Néanmoins on peut avoir besoin de write.

Exemple

 cout.write (ch, n) qui affiche les n premiers caractères de ch.

1-9
Chapitre

1 - 10
Adresses et pointeurs

Adresses
L'unité de stockage en mémoire est l'octet. Chaque octet de la mémoire centrale est repéré par une adresse.
Toute variable d'un programme fait l'objet lors de sa déclaration d'une allocation de mémoire ; le compilateur
en fonction du type de la variable lui associe les octets nécessaires pour représenter les valeurs qu'elle peut
prendre. L'adresse du premier octet alloué devient l'adresse de la variable.

Exemple :

void main () {
short int i, j; // sur 2 octets
char c; // sur 1 octet
float f; // sur 4 octets

}

Pointeur
Un pointeur est une variable dont la valeur est une adresse de la mémoire centrale.
Soient i un entier initialisé à 2 et p une variable de type pointeur sur entier, si p reçoit pour valeur
l'adresse de i, on dit alors que p pointe sur i. On peut accéder à la valeur 2 soit directement par i, soit par
l'intermédiaire de p (voir ci-dessous).

Déclaration
Lors de sa déclaration, on indique pour chaque pointeur le type de la valeur pointée.

Syntaxe

TYPE * IDENTIFICATEUR

Exemple

int * p; // p est déclaré pointeur sur entier,


// il pourra contenir l'adresse de toute variable entière

2-1
Opérateurs & et *
Opérateur d'adressage &
L'opérateur d'adressage permet de désigner à partir d'une variable son adresse.

Exemple
int i = 0;
int * p;
p = &i; // p a désormais pour valeur l'adresse de i

Opérateur d'indirection *
L'opérateur d'indirection * permet d'accéder à la valeur pointée à partir du pointeur.

Exemple
int i = 4;
int * p;
q = &i;
i = *q + 5; // équivaut à i = i + 5;
*q = i + 2; // équivaut à i = i + 2;

Passage de pointeurs en paramètres

#include <iostream.h>

void echange (int * a, int * b);

void main(){
int i=1, j=2;
echange(&i, &j);
cout << "i= " << i << " et j= " << j << endl;
}

void echange (int * a, int * b){


int i;
i = *a;
*a = *b;
*b = i;
}

Pointeurs et autres types


Pointeurs et tableaux
Le nom d'un tableau est en réalité un pointeur sur le premier élément du tableau.
L'adresse du premier élément du tableau se note indifféremment T ou &T[0].
L'adresse du i-ème élément est &T[i] ou T+i
Attention l'opérateur + dans une expression où se trouve un pointeur a un effet qui dépend du type des
valeurs pointées. L'expression T+i correspond au calcul de T+i*sizeof(T[0]) .
Pointeurs et structures
Pour les pointeurs sur une structure, il existe un opérateur, noté -> qui relie le pointeur de la structure au
champ que l'on désire atteindre. Cet opérateur a introduit une simplification d'écriture comme le
montrent les deux lignes (8 et 9) équivalentes de l'exemple ci-dessous.

Syntaxe
POINTEUR -> CHAMP

Exemple

typedefstruct {
int numero;
char nom[10];
} t_client;

void main () {
t_client c,* pc;
pc = &c;
(*pc).numero=6; // ligne 8
pc->numero=6; // ligne 9
}

Pointeurs et références
Le programme vu plus haut aurait pu s'écrire :

#include <iostream.h>

void echange (int & a, int & b);

void main(){
int i=1, j=2;
echange(i, j);
cout << "i= " << i << " et j= " << j << endl;
}

void echange (int & a, int & b){


int i;
i = a;
a = b;
b = i;
}

On peut aussi déclarer des variables de type référence.

Exemple

void main () {
int i;
int &ri=i;
i++;
ri++;
cout << i << " " << ri;
}
A l b e r t R O Y E R I U T S T I D P a u

Chapitre

3
A l b e r t R O Y E R I U T S T I D P a u

Les flux
On appelle flux (stream) le flot de données qui est produit par une
source (ex : clavier) et consommé par une cible (ex : unité centrale).

ar défaut, sont définis trois flots :

 cout qui correspond à l’unité standard de sortie, en général l’écran,

 cin qui est associé à l’unité standard d’entrée, a priori le clavier,

 cerr qui représente l’unité standard d’erreur, par défaut l’écran.

Les flots peuvent être attachés à des tableaux ou à des fichiers. Les opérations d’entrée-sorties qui
manipulent ces flots ne font pas partie des instructions du langage, elles sont définies dans une
bibliothèque (d’où l’utilisation d’include en début de programme).

Remarque : Dans ce chapitre nous ne nous intéresserons qu’aux fichiers .

Fichiers
Généralités
Suite d’octets
Tout fichier en C++ est vu comme une suite d’octets. Si tous les octets d’un fichier sont imprimables (i.e.
caractères alphanumériques ou caractères assimilables à une espace comme la tabulation, le retour-
chariot, ...), le fichier est un fichier texte. Dans le cas contraire, il correspond à un fichier binaire et peut
servir à implanter des fichiers typés.

fstream.h
Toute manipulation de fichiers fait appel aux primitives prévues sur les flux définies dans fstream.h et,
donc, tout programme commencera par #include <fstream.h>

Type d’utilisation de fichiers


On distingue trois catégories de fichiers selon le type d’utilisation que l’on en fait : lecture seule, écriture
seule, ou, lecture et écriture. Dès la déclaration des noms logiques (= internes) des fichiers, il faudra
choisir son type.

Exemples de déclaration de fichiers


#include <fstream.h>

1. ifstream ENTREE ; // pour lecture seule

2. ofstream SORTIE ; // pour écriture seule

3-1
3. fstream FICHIER ; // pour lecture et écriture

Opérations communes à tous les fichiers


Ouverture du fichier
Quelque soit la nature de l’utilisation du fichier, l’ouverture est réalisée par open. Elle fait le lien entre le
flux et le nom logique du fichier.

Exemples d‘ouverture de fichiers

1. ENTREE.open(donnees.dat);
// pour lecture seule

2. SORTIE.open(result.dat) ;
// pour écriture seule

3. FICHIER.open(texte.txt,input |
output) ; // en lecture / écriture

4. FICHIER.open(texte.txt,appe
nd) ; // en ajout

5. FICHIER.open(texte.txt,
atend) ; // positionne le curseur à la fin

Fermeture du fichier
Quelque soit la nature de l’utilisation du fichier, la fermeture est réalisée par close. Elle rompt le lien
entre le flux et le nom logique du fichier.
Contrôle de l’état d’un flux
Il y a plusieurs façons de contrôler l’état d’un flux :

 int bad()
renvoie une valeur non nulle si la dernière opération d’E/S est interdite,

 int eof()
renvoie une valeur non nulle si la fin de fichier est atteinte,

 int fail()
renvoie une valeur non nulle si la dernière opération d’E/S a échoué,

 int good()
renvoie une valeur non nulle si la dernière opération d’E/S a réussi,

 int rdstate()
renvoie la valeur correspondant à l’état du flux (0 signifie OK),

 int clear()

3-2
A l b e r t R O Y E R I U T S T I D P a u

remet à zéro l’indicateur du flux ; cette opération est à lancer après qu’une erreur se soit produite si
l’on veut traiter l’erreur dans le programme.

3-3
Exemples de tests

1. if (ENTREE.good()) ...; // s’écrit


aussi if (ENTREE) ...
2. if ( ! SORTIE.bad()) ... ;

3. while ( ! FICHIER.eof() ) ...; //


tantque non fin de FICHIER ...

Opérations de manipulation de fichiers


Fichier ofstream
En plus de l’opérateur d’injection (<<), on dispose des six opérations suivantes :

 put(char c) qui insère un caractère dans le flot ;

 write(const char *, int n) qui insère n caractère dans le flot ;

 streampos tellp() renvoie la position courante de l’index du fichier ; le type streampos


est déclaré dans fstream.h , il définit le type des valeurs du curseur et il équivaut à un entier
long ;

 seekp(streampos n) positionne l’index du fichier à n octets depuis le début ; le premier


octet est au rang 0 ;

 seekp(streamoff dep, seek_dir dir) qui positionne l’index à dep octets par
rapport au début de fichier (dir=beg), ou à la position courante de l’index ( dir=cur) ou
encore à la fin du fichier (dir=end et dans ce cas dep<0) ; le type streamoff est déclaré
dans fstream.h , il définit le type des valeurs du déplacement et il équivaut à un entier long ;

 flush() qui vide les tampons du flux.

Exemples d’utilisation
1. SORTIE.put(‘\n’) ;

2. SORTIE.write (Voilà,5) ;

3. streampos POSITION = SORTIE.tellp() ;

4. SORTIE.seekp(0L, ios::end) ;

cout << Taille << SORTIE.tellp() << octets;

5. SORTIE.flush() ;

3-4
A l b e r t R O Y E R I U T S T I D P a u

Fichier ifstream
En plus de l’opérateur d’extraction (>>), on dispose des onze opérations suivantes :

 int get() qui renvoie la valeur du caractère lu (ou EOF si on est en fin de fichier) ;

 get(char &c)extrait le premier caractère et le place dans c (même une espace) ;

 int peek() lecture non destructrice d’un caractère ;

 get(char *ch, int n, char delim=’\n’) qui extrait n-1 caractères et les place à
l’adresse ch, la lecture s’arrête éventuellement à la rencontre du caractère ‘\n’ ou de la fin de
fichier ; le délimiteur n’est pas extrait du flux ;

 getline(char *ch, int n, char delim=’\n’) est identique à get avec cette
différence que le délimiteur est extrait du flux, il n’est pas copié dans ch ;

 read(char *ch, int n) extrait au maximum n octets et les place à l’adresse ch ; le


nombre d’octets effectivement lus peut être obtenu par gcount ;

 int gcount() retourne le nombre de caractères extraits lors de la dernière lecture ;

 streampos tellg() renvoie la position courante de l’index du fichier ; le type streampos


est défini dans fstream.h ;

 seekg(streampos n) positionne l’index du fichier à n octets depuis le début ; le premier


octet est au rang 0 ;

 seekg(streamoff dep, seek_dir dir) qui positionne l’index à dep octets par rapport
au début de fichier (dir=beg ), ou à la position courante de l’index ( dir=cur ) ou encore à la
fin du fichier (dir=end et dans ce cas dep<0) ;

 flush() qui vide les tampons associés au flux ;

 putback(char c) qui remet le caractère non désiré dans le fichier ;

Exemples d’utilisation
1. ENTREE.get(CARACTERE) ;

2. char LIGNE[256] ; ENTREE.read(LIGNE,256) ;

3-5
Fichier fstream
À la différence des deux types précédents, les fichiers fstream peuvent s’utiliser en lecture ou (inclusif)
écriture. On peut donc utiliser à la fois les opérations définies pour ifstream et celles pour
ofstream.

Accès direct
À un octet paticulier
Puisque les fichiers sont vus comme une suite d’octets, l’accès direct se fait par déplacement du curseur
en nombre d’octets ou bien depuis le début du fichier, ou bien à partir de la position courante du curseur
ou encore par rapport à la fin du fichier.

On positionne le curseur sur le premier octet concerné par l’opération à venir à l’aide de seekp (seek
for put), respectivement seekg (seek for get), s’il s’agit d’une opération d’écriture, respectivement de
lecture.

Exemples d’utilisation
1. ENTREE.seekg(80L, ios::cur);
ENTREE >> NOMBRE; // saute 80 octets pour lire NOMBRE

2. ENTREE.seekg(0L, ios::end) ;
cout << "Taille : " << ENTREE.tellg() << " octets ";

3. SORTIE.seekp(long(P), ios::beg); SORTIE << C;


// modifie le P-ième octet du fichier

À un enregistrement
Bien que la notion d’enregistrement n’existe pas, elle peut être simulée en utilisant l’opérateur sizeof
qui donne la taille d’un type ou d’une variable. Si on veut manipuler des fichiers d’individus où chaque
enregistrement est décrit par une structure INDIVIDU, on peut positionner le curseur sur l’enregistrement
concernant le n-ième individu du fichier par
FICHIER.seekg(long(sizeof(INDIVIDU)*(n-1)), ios::beg);

Puis, on demande à effectuer l'entrée-sortie désirée, soit pour une lecture :

FICHIER.read((char *) (&C), sizeof(INDIVIDU));

Présentation des informations


Formatage
Dans stream.h est définie une fonction appelée form qui permet de préciser par un format l’écriture
des informations dans un fichier texte, voire de l’écran. La suite d’exemples illustre les principales
possibilités offertes :

Exemples d’utilisation
1 cout << form("%3d" , nb);
// affiche nb sur 3 positions cadré à droite

3-6
A l b e r t R O Y E R I U T S T I D P a u

2 SORTIE << form("%5.2f", hauteur);


// la valeur de hauteur est écrite sur les 5 prochains octets
// les 2 derniers servant à la partie décimale

2 SORTIE << form("%20s", ch);


// met dans SORTIE les 20 premiers caractères de ch

3 FICHIER << form("%c", (X+Y)%256);


// copie dans FICHIER le caractère
// dont le code est donné par (X+Y) % 256

4 cout << form("Le résultat est \t %d\n", i);

Manipulateurs
En plus de endl, il existe :

 dec pour saisir ou écrire un entier en décimal


exemple : cout << dec << 136
affiche 136

 oct pour saisir ou écrire un entier en octal


exemple : cout << oct << 136
affiche 210

 hex pour saisir ou écrire un entier en hexadécimal


exemple : cout << hex << 136
affiche 88

 setw(n) pour afficher n caractères exemple : cout


<< setw(8) << 136 affiche
136

 setprecision (n) afficher la valeur avec n chiffres

 setfill(caractère) pour définir le caractère de remplissage


exemple : cout << setfill('*') << setw(8) << 136
affiche *****136

 flush pour vider le tampon après écriture

 ends écrit un '\0' et vide le flot

 ws saute les espaces (en entrée uniquement)

On notera que seuls les manipulateurs nécessitant des paramètres sont déclarés dans iomanip .h.

Pour en savoir plus


Autres modes d'ouverture
Les indications de mode d’utilisation sont définies dans le type énuméré de la classe ios, ce sont :

3-7
 app // ajout de données en fin de fichier,
// redéfini en append

 ate // positionnement en fin de fichier,


// redéfini en atend

 in // ouverture en lecture seule, par défaut pour


// ifstream, redéfini en input

 out // ouverture en écriture seule, par défaut pour


// ofstream, redéfini en output

 trunc // écrase le fichier existant

 nocreate // le fichier doit exister

 noreplace // le fichier ne doit pas exister.

Exemples d’utilisation
1. SORTIE.open("fich.dat", ios::noreplace);
// si le fichier existe, l'ouverture échoue

2. FICHIER.open(texte.txt, ios::nocreate);
// si le fichier n'existe pas, l'ouverture échoue

3. SORTIE.open(texte.txt, ios::trunc);
// détruit le fichier s'il existe et le recrée

Définition des opérateurs d'entrée-sortie


Pour mieux visualiser dans les fichiers textes, la notion d'enregistrement, il est souhaitable de se
définir les opérateurs d'extraction et d'injection. Il est nécessaire de passer par la définition de
classe.

Exemple pour le type


struct SAUT { int ESSAI_NO ; float HAUTEUR ; }

Exemple de définition de l'opérateur d'extraction

class SAUT_ES {
public :
friend ofstream& operator << (ofstream& os, const SAUT_ES &S) ;
// ...
private :
int ESSAI_NO ;
float HAUTEUR ;
} ;

ofstream& operator << (ofstream& os, const SAUT_ES &S){


return os << S.ESSAI_NO << " essai à " << S.HAUTEUR ;
}

3-8
A l b e r t R O Y E R I U T S T I D P a u

3-9
Exemple d'utilisation

void main(){
ofstream SORTIE ;
SAUT_ES S1 ;
// ...
SORTIE << " exemple de saut " << S1 << endl ;
}

Exemple de définition de l'opérateur d'injection

class SAUT_ES {
public :
friend ifstream& operator >> (ifstream& is, const SAUT_ES &S) ;
// ...
private :
int ESSAI_NO ;
float HAUTEUR ;
} ;

ifstream& operator >> (ifstream& is, SAUT_ES &S) {


return is >> S.ESSAI_NO >> S.HAUTEUR ;
}

Exemple d'utilisation

void main(){
ifstream ENTREE ;
SAUT_ES S1 ;

ENTREE >> S1 ;
// ...
}

3 - 10
A l b e r t R O Y E R I U T S T I D P a u

Chapitre

4
A l b e r t R O Y E R I U T S T I D P a u

Les objets
On appelle objet, toute entité individuelle, repérée par une adresse unique
constituée de plusieurs champs, connus par leurs noms (en nombre fixé). Un objet
réagit à des messages qui lui sont envoyés en exécutant les procédures qui leur
correspondent.

Classe
La classe est un texte enfermant sous un même nom tous les éléments définissant une même
catégorie d'objets (encapsulation).

Membre de classe
Une classe d'objets est caractérisée par ses membres. Il y a deux sortes de membres :
Données membres
Les données membres (data members) sont les variables qui rendent compte de l'état de l'objet. On
pourra adopter la notation préfixée par un 'souligné' pour ces variables. Dans l'exemple de la classe
fraction, il y en a deux : _num et _den.

Fonctions membres
Les fonctions membres (member functions) sont les procédures qui réagiront aux messages. Dans
l'exemple fraction, ce sont : init, numerateur , denominateur et normaliser .

visibilité des membres


Les membres d'une classe sont
ou privés (private), et ne pourront être manipulés explicitement que par les procédures de la
classe, exemple : normaliser
ou publiques (public), et pourront être référencés, partout où il est fait référence à une
instance de la classe, exemple : f.denominateur()
ou protégés (protected) (ne sera pas vu ici).

4-1
Exemple :

Déclaration de la classe fraction


La déclaration d'une classe constitue une définition de type. Il est commode de la faire figurer dans un
fichier d'en-tête( header file), ici fraction.h

class Fraction {
public :
void init (int n=1, int d=1); // initialise la fraction n/d
int numerateur(void); // renvoie le numérateur de la fraction
int denominateur(void); // " le dénominateur de la fraction
private :
Fraction normaliser (void); // réduit la fraction,
// met le signe au numérateur
int _num; // entier qui sera signé
int _den; // entier positif
};

Définitions des opérations


Les textes complets des procédures répondant aux messages sont donnés dans fraction.cpp

void Fraction::init (int n=1, int d=1){


_num = n;
_den = d;
}

int Fraction::numerateur(void){return _num;}

int Fraction::denominateur(void) {return _den;}

Fraction Fraction::normaliser (void){ …};

Utilisation de la classe fraction


Voici un exemple de programme manipulant un objet de type fraction, i.e. une instance de cette classe.
L'instance est constituée des attributs dont la liste figure dans la classe (données membres). Son
comportement est défini par les procédures de la classe (fonctions membres).

#include <iostream.h>
#include "fraction.h"

void main () {
int num, den;
Fraction f; // déclare f, instance de la classe Fraction
cout << "Numérateur et dénominateur de la fraction ";
cin >> num >> den;
f.init(num, den); // envoi du message init à l'objet f
cout << "La fraction normalisée est : "<<endl;
cout << f.numerateur() << " / " << f.denominateur() << endl;
}

4-2
A l b e r t R O Y E R I U T S T I D P a u

Héritage
Dérivation
Une classe D hérite d'une classe B si tous les attributs définis par B se retrouvent dans D.
En C++, l'héritage s'appelle dérivation. D est la sous-classe ou classe dérivée. B est la sur-classe ou
classe de base.

Exemple :
neofraction.h

class NeoFraction : public Fraction { // classe dérivée de Fraction


public :
int egal (NeoFraction &f);
// ajoute aux opérations disponibles la comparaison d'égalité
};

neoprincipal.cpp

#include <iostream.h>
#include "neofraction.h"

void afficher(f);

void main () {
int num, den;
NeoFraction f1, f2;
cout << "Numérateur et dénominateur de la première fraction ";
cin >> num >> den;
f1.init(num, den); // envoi du message init à l'objet f1
cout << "Numérateur et dénominateur de la deuxième fraction ";
cin >> num >> den;
f2.init(num, den); // envoi du message init à l'objet f2
afficher(f1);
if (f1.egal(f2))
cout << " est égal à ";
else
cout << "est différent de ";
afficher(f2);
cout << endl;
}

void afficher(f){
cout << f.numerateur() << " / " << f.denominateur();
}

neofraction.cpp

int NeoFraction::egal (NeoFraction &f) {


return numerateur()*f.denominateur()==f.numerateur()*denominateur();
};

4-3
Constructeur

Les constructeurs sont chargés de la création et de l'initialisation des instances.

Exemple :

fraction.h

class Fraction {
public :
Fraction (int n=1, int d=1); // constructeur de la fraction n/d
int numerateur(void); // renvoie le numérateur de la fraction
int denominateur(void); // " le dénominateur de la fraction
private :
Fraction normaliser (void); // réduit la fraction,
// met le signe au numérateur
int _num; // entier qui sera signé
int _den; // entier positif
};

programme principal

#include <iostream.h>
#include "fraction.h"

void main () {
Fraction f(4, 7), g; // f=4/7 et g=1/1
cout << "Les fractions normalisées sont : "<<endl;
cout << f.numerateur() << " / " << f.denominateur() << endl;
cout << g.numerateur() << " / " << g.denominateur() << endl;
}

fraction.cpp

Fraction::Fraction (int n=1, int d=1){


_num = n;
_den = d;
}

int Fraction::numerateur(void){return _num;}


int Fraction::denominateur(void) {return _den;}

Fraction Fraction::normaliser (void){ …};

4-4
A l b e r t R O Y E R I U T S T I D P a u

Amis

Pour permettre à certaines fonctions et à certaines classes d'accéder à tous les éléments d'une
classe, ce qui revient à disposer d'un moyen de violer l'abstraction des données, il suffit de les
déclarer amies (friends) de la classe.

Exemple :
fraction.h
Cette classe contient deux fonctions amies qui sont les opérateurs d'entrée et de sortie.

class Fraction {
public :
Fraction (int n=1, int d=1); // constructeur de la fraction n/d
int numerateur(void); // renvoie le numérateur de la fraction
int denominateur(void); // " le dénominateur de la fraction
friend istream& operator >> ( istream& is, Fraction &f);
friend ostream& operator << ( ostream& os, const Fraction &f);
private :
Fraction normaliser (void); // normalise la fraction,
int _num; // entier qui sera signé
int _den; // entier positif
};

fraction.cpp
Les fonctions amies ont accès aux membres de la classe.

Fraction::Fraction (int n=1, int d=1){ _num = n; _den = d;}

int Fraction::numerateur(void){return _num;}


int Fraction::denominateur(void) {return _den;}

istream& operator >> ( istream& is, Fraction &f) {


// permet la saisie sous la forme n/d
char c; // sert à avaler le signe /
is >> f._num >> c >> f._den;
return is;
}

ostream& operator << ( ostream& os, Fraction &f) {


// permet l'affichage sous la forme n/d
return os << f._num << '/' << f._den;
}

Fraction Fraction::normaliser (void){ …};

4-5
programme principal
Les fonctions amies de la classe fraction sont utilisées sans être préfixées par le nom d'une instance de
cette classe.

#include <iostream.h>
#include "fraction.h"

void main () {
Fraction f, g; // f=1/1 et g=1/1
cout << "Entrez deux fractions "<<endl;
cin >> f >> g;

}

Conclusion
Nous nous sommes limités ici aux premières notions sur les objets. Il y aurait lieu
d'approfondir la notion de classe en parlant de membres protégés, de destructeur, d'héritage
multiple et de classes génériques.

4-6
A l b e r t R O Y E R I U T S T I D P a u

Annexes
A l b e r t R O Y E R I U T S T I D P a u

fract2.cpp
#include <iostream.h>
#include <iomanip.h>

// fract2.cpp
// deuxième version du programme implantant les fractions
// -créé par A.Royer le 23 juil.97

// déclaration du type FRACTION


struct FRACTION
{
int num;
//entier signé
int den;
//entier positif
};

//déclaration des prototypes des différentes fonctions du programme

FRACTION saisir(void);
void afficher(FRACTION f);

FRACTION plus(FRACTION f1, FRACTION f2);

int pgcd(int a, int b);

void normaliser(FRACTION & f);

void main()
{
FRACTION f1, f2, f3;

cout << "Entrer la première fraction \n";


f1 = saisir();
cout << "Entrer la deuxième fraction \n" << endl;
f2 = saisir();
// pour éviter d 'allonger le texte, on ne programme que l' addition
f3 = plus(f1, f2);
cout << "Le résultat est : ";
afficher(f3);
cout << endl;
}

//définition des fonctions

FRACTION saisir(void)
{
//valeur de retour : la fraction saisie
FRACTION f;

cout << "Numérateur ? ";


cin >> f.num;
cout << "Dénominateur ? ";
cin >> f.den;

a-1
A l b e r t R O Y E R I U T S T I D P a u

return f;
}

a-2
void afficher(FRACTION f)
{
//en entrée : la fraction à afficher
cout << f.num << " / " << f.den;
}

FRACTION plus(FRACTION f1, FRACTION f2)


{
//en entrée : deux fractions
// valeur de retour : la fraction somme des deux précédentes
FRACTION f;

f.den = f1.den * f2.den;


f.num = f1.num * f2.den + f2.num * f1.den;
normaliser(f);
return f;
}

int pgcd(int a, int b)


{
//en entrée : deux entiers, valeur de retour : le pgcd des 2 nombres

//on ne s'intéresse qu'aux valeurs absolues des paramètres


if (a < 0)
a = -a;
if (b < 0)
b = -b;

if (a * b == 0)
//cas particulier où au moins un des deux nombres est nul
return a + b;
else
while (a != b)
//cas général
if (a > b)
a = a - b;
else
b = b - a;
return a;
}

void normaliser(FRACTION & f)


{
//en entrée et en sortie : la fraction objet de la normalisation
int d;

//traitement du signe du dénominateur et réduction de la fraction


if (f.den < 0)
{
f.num = -f.num;
f.den = -f.den;
}
d = pgcd(f.num, f.den);
f.num = f.num / d;
f.den = f.den / d;
}