Cours
Cours
Résumé de cours
X. CARON
1
PLAN
LE LANGAGE C
Introduction 3
Commentaires 3
Déclarations 3
Notion de bloc 4
Variables 4
Constantes nommées 5
Conversion de variables (transtypage) 5
Opérateurs de comparaison 5
Test 5
Boucles 6
Aiguillages 7
Opérateurs d'incrémentation 7
Les fonctions 7
Ordres de précompilation 8
Tableaux à une dimension 8
Tableaux à plusieurs dimensions 9
Pointeurs 9
Passage d’un tableau en paramètre dans une fonction 9
Allocation statique et allocation dynamique 9
Les entrées-sorties en C 10
LE LANGAGE C++
Spécificités du C++ 12
Passage de données 12
Notions de classe et d'objet 13
La classe, le constructeur, le destructeur 13
Les paramètres par défaut 15
Les variables statiques 16
L'allocation dynamique et le constructeur de recopie 16
Les fonctions amies 18
La redéfinition des opérateurs 18
Les patrons de classe 19
L'héritage (la POO) 20
Méthodes virtuelles 21
Classes virtuelles 22
Les flots d'entrée-sortie en C++ 22
Références 22
Exercices d'application 23
2
LE LANGAGE C
Le langage C++ utilisant comme base syntaxique le langage C, ce chapitre en rappelle quelques notions.
Le langage C a été créé en 1973 par l’équipe de Dennis Ritchie pour la mise au point du système
d’exploitation UNIX.
Il y a plusieurs normes pour ce langage. Ce document présente la norme ANSI agrémentée de facilités
d’écriture provenant du C++ (d’où l’obligation d’utiliser un compilateur C++).
* Introduction :
* Commentaires :
* Déclarations :
Il y a toujours un point-virgule (;) pour signifier la fin d'une instruction ce qui permet d'éventuellement
mettre autant d'instructions que l'on veut sur une même ligne.
3
En C++, on place les déclarations là où on en a besoin :
void main(void)
{
int a;
a=1;
float b;
b=2.0f;
}
* Notion de bloc :
* Variables :
long double : réel 80 bits (10 octets) signé ±1.2 10 (19 décimales)
±4932
Les caractères :
char : caractère signé (entier sur 8 bits)
unsigned char : caractère non signé (entier positif sur 8 bits)
Exemple :
int a,b; // déclaration de a et b entiers
a=2; // définition de a
long i,j=1; // i et j entiers longs et j est mis à 1 (i est implicitement mis à 0)
float c=3.f; // c est un réel. La lettre f après une constante flottante indique
4
// une constante de type : float
int m=1,n; // m et n sont des entiers et m est mis à 1
i=j+2l; // l ou L après un entier indique un entier long
i=j+2L; // (idem pour avoir un flottant long : 2.36L)
* Constantes nommées :
L'instruction const placé devant une déclaration permet de faire d'une variable une constante de
manière à être sûr qu'elle ne soit pas modifiée par la suite. Cette notion sera très utile en C++ où la
protection des variables est un point essentiel.
Exemple : const int toto = 10; // toto est une constante entière de valeur 10
toto = 5; // cette ligne provoque une erreur à la compilation
// car toto ne peut pas être modifiée
Une variable d'un type donné peut avoir besoin d'être convertie dans un autre type.
Exemple :
float a = 1.f; // a est un réel
int b = 2; // b est un entier
int c; // c est un entier
c = b + a; // cette ligne provoquera un "warning" à la compilation car on additionne
// un réel et un entier
c = b + int(a); // cela passe à la compilation car on convertit a en entier
c = b + (int)a; // autre écriture possible pour la conversion
* Opérateurs de comparaison :
* Test :
if (condition)
instruction 1
else
instruction 2
Si la condition est satisfaite, alors l'instruction 1 est exécutée, sinon l'instruction 2 est exécutée.
Exemple très classique : résolution de l'équation du second degré
void main (void)
{
5
float a,b,c;
a=1.f; b=2.f; c=3.f;
float delta,rac1,rac2,rac;
delta = b*b – 4.f*a*c;
if (delta >= 0.f)
if (delta == 0.f)
rac = -b/2.f/a;
else
{
rac1 = (-b-sqrt(delta))/2.f/a;
rac2 = (-b+sqrt(delta))/2.f/a;
}
}
* Boucles :
Il y a 2 façons de faire une boucle : soit avec un for soit avec un while (tant que). Le while ne se
jusitifie que si le for n’est pas bien adapté au problème. En général « un for avec un if est un while ».
Dans ce cas le while permet d’éviter l’utilisation de l’instruction break (voir l’exemple qui suit).
Syntaxes :
for ( instruction1 ; condition ; instruction2 ) instruction3
Possibilité de sortie de boucle : dès la rencontre de l'instruction break.
while (expression) instruction ou do instruction while (expression);
Tant que l'expression est satisfaite, exécuter l'instruction. La seconde écriture permet d'avoir au
moins une exécution même si l'expression est fausse au départ.
Exemple : Soit un point de coordonnées (xd,yd) situé dans un rectangle de sommets (0,0) et
(xmax,ymax). On déplace le point de a suivant x et b suivant y. On veut arrêter la boucle lorsque 1000
déplacements ont été effectués ou lorsque le point sort du rectangle.
void main (void)
{
float xd=1.f , yd=1.f , xmax=10.f , ymax=10.f;
float x , y , a=.1f , b=.2f;
// au moins 4 écritures équivalentes :
int i;
x = xd; y = yd;
for ( i=0 ; i<1000 ; i=i+1)
{
x = x + a;
y = y + b;
// le break force à sortir de la boucle
if ( x<0 || x>xmax ) break;
if ( y<0 || y>ymax ) break;
}
// OU
x = xd; y = yd;
for ( int i=0 ; i<1000 ; i++)
{
x += a;
y += b;
if ( x<0 || x>xmax ) break;
if ( y<0 || y>ymax ) break;
6
}
// OU
for ( x=xd, y=yd, int i=0 ; i<1000 && x>0 && x<xmax && y>0 && y<ymax ;
i++ , x+=a , y+=b);
// OU
int i=0;
x=xd; y=yd;
while ( i<1000 && x>0 && x<xmax && y>0 && y<ymax )
{
i ++;
x += a;
y += b;
}
}
* Aiguillages :
Dans le cas où une variable aiguille sur plusieurs cas suivant sa valeur, on peut écrire :
switch (toto) // suivant la valeur de toto
{
* Opérateurs d'incrémentation :
i ++ et ++ i sont équivalents à : i = i + 1
Différence entre les deux écritures :
opérateur ++ avant : on incrémente avant la prise de valeur
opérateur ++ après : on incrémente après la prise de valeur
exemple :
for ( i=0 ; i++ <= 10 ; ) cout << i; // i varie de 1 à 11 (à la sortie : i=12)
for ( i=0 ; ++i <= 10 ; ) cout << i; // i varie de 1 à 10 (à la sortie : i=11)
* Les fonctions :
Une fonction possède des paramètres d'entrée fournis entre parenthèses et un paramètre de sortie.
Elle doit être écrite en dehors du programme principal. Chaque fonction doit avoir un prototype. Il sert
à décrire le type des variables en entrée et en sortie. On doit obligatoirement le placer avant l’appel de
la fonction, dans le bloc où se trouve cet appel.
Exemple :
#include <iostream.h> // pour pouvoir faire des sorties à l'écran (voir C++)
void main (void)
{
7
float carre (float); // prototype de la fonction carre
// placé avant l'appel. Ne lance rien à l'exécution.
float x,ca;
cin >> x; // entrée de x au clavier
ca = carre(x); // appel de la fonction carre
cout << "Le carré de " << x << " est égal à " << ca << "\n";
}
float carre(float a)
{
float res;
res = a*a;
return (res);
}
* Ordres de précompilation :
Les ordres de précompilation sont précédés du signe #. Ils sont exécutés avant la phase de
compilation.
#include <toto.h> // inclus le fichier toto.h qui se trouve dans le répertoire racine
#include "toto.h" // inclus le fichier toto.h qui se trouve dans le répertoire courant
Par exemple, pour ne compiler qu'une seule fois une partie d'un programme :
#ifndef TOTO // si la variable TOTO n'est pas définie, on fait la suite
#define TOTO // défini la variable TOTO
......
#endif // fin du ifndef
8
* Tableaux à plusieurs dimensions :
* Pointeurs :
A une variable sont associées deux notions : son adresse en mémoire et son contenu. Un pointeur
permet d'accéder à une donnée quelconque (variable, fonction, objet, ...) par l'intermédiaire de son
adresse en mémoire.
Exemple :
#include <iostream.h>
void main (void)
{
float x = 1.f; // x est un flottant
float *toto; // toto est un pointeur de flottant
toto = &x; // toto contient l'adresse de x
9
Il y a deux manières de déclarer une variable ou un tableau. La manière « classique » consiste à
déclarer un tableau ou une variable avec l’allocation statique. Si on a besoin de contrôler la durée de vie
de la variable ou du tableau on utilise l’allocation dynamique. En C elle est réalisée avec les fonctions
malloc et desalloc. En C++ ce sont les fonctions new et delete. Ces instructions en C et en C++ sont
incompatibles entre elles. Pour cette raison nous n’utilisons ici que l’allocation dynamique en C++.
Voici un résumé des différentes possibilités :
* Les entrées-sorties en C :
Pour des raisons de familiarité avec le C, certains programmeurs utilisent encore les E/S (entrées-
sorties) dans ce langage. Il faut cependant se rendre à l'évidence que les E/S par flots en C++ ont une
garantie beaucoup plus grande au niveau de la sécurité et qu'elles assurent une syntaxe identique
quelque soit le type d'objet à sortir.
Les ordres d'E/S en C nécessitent l'insertion du fichier contenant les prototypes :
#include <stdio.h> // STanDard Input Output
Exemple :
longueur = 10;
printf (" Longueur = %d mètres", longueur);
Affichage à l'écran :
Longueur = 10 mètres
%f flottant
%nf flottant sur n caractères (le signe moins(-) et le point (.) sont des caractères)
%[Link] flottant sur n caractères et d décimales
10
%c caractère
%s chaîne de caractères
%ns chaîne sur n caractères minimum
11
LE LANGAGE C++
La programmation en C++ est une Programmation Orientée Objet (POO). Le C++ n'apporte au
vocabulaire du C qu'une dizaine de mots supplémentaires.
Le langage C++ a été créé en 1983 par un universitaire : Bjarne Stroustrup. Il obéit à une norme ANSI.
* Spécificités du C++ :
- Fonctions en ligne :
Il est possible, au lieu d'écrire un prototype et plus loin la fonction, d'écrire directement la fonction
en ligne à l'endroit où se trouve le prototype à l'aide du mot-clé inline de façon à améliorer le temps
d'exécution.
Par exemple :
inline float carre (float a) { return (a*a);}
* Passage de données :
12
#include <iostream.h>
void main (void)
{
float x = 1.f; // x est un flottant
void calcule (float *); // prototype de la fonction
cout << x; // affichage de x
calcule (&x); // appel dela fonction
cout << x; // affichage de x
}
void calcule (float *a)
{
*a = 2;
}
affichage avant l'appel de la fonction : 1
affichage après l'appel de la fonction : 2
Il y a un effet de bord, la variable x est modifiée au retour par l’intermédiaire de son adresse.
L'idée de base est que des langages du type FORTRAN ou C permettent de manipuler et de déclarer
uniquement des nombres ou des caractères. La Programmation Orientée Objet va plus loin et permet de
manipuler des nombres, des caractères mais aussi des objets quelconques.
Qu'est-ce qu'une classe ?
Une classe définit un ensemble d'objets.
Qu'est-ce qu'un objet ?
C'est n'importe quoi dans les limites de l'imagination du programmeur.
13
Fichier ma_classe.h :
# ifndef MA_CLASSE
# define MA_CLASSE
class ma_classe
{
private :
... // données, informations, objets
...
public :
int tourner (void);
... // prototypes des méthodes qui manipulent,
... // modifient les objets, les données
...
};
# endif
Fichier ma_classe.cpp :
#include "ma_classe.h"
int ma_classe::tourner (void) // méthode tourner
{
int a;
...
...
return (a);
}
Fichier mon_programme_principal.cpp :
#include "ma_classe.h"
void main(void)
{
ma_classe toto;
[Link]( );
}
Une classe possède une zone de données privées (private) qui ne sont accessibles que par la classe elle-
même ce qui assure la protection totale de ces données vis-à-vis des autres classes et des utilisateurs
de la classe. Ceux-ci n'ont accès qu'à la partie publique (public) de la classe.
Exemple : on veut écrire une classe appelée : homme qui permet de définir et faire évoluer un homme.
Fichier homme.h :
# ifndef HOMME
# define HOMME
class homme
{
private :
int age; // age de l'homme en années
float taille; // taille de l'homme en mètres
public :
14
homme ( int, float); // CONSTRUCTEUR. Il se trouve obligatoirement
// en public.
~homme (void); // DESTRUCTEUR.
void vieillir (void); // méthode qui fait vieillir d'un an.
};
# endif
Fichier [Link] :
#include "homme.h"
homme::homme (int i, float a)
{
age = i;
taille = a;
}
homme::~homme (void)
{}
Fichier mon_programme_principal.cpp :
#include "homme.h"
void main(void)
{
homme robert(20,1.7); // on lance le constructeur
[Link]( ); // on lance la méthode vieillir
} // la fin du bloc lance le destructeur
Du fait de la possibilité de la surdéfinition des fonctions, il peut et il est même conseillé d'avoir
plusieurs constructeurs.
15
homme::homme (int i, float a)
{
age = i;
taille = a;
}
on pourra écrire :
class homme
{ private :
…
public :
homme ( int = 20, float = 1.7f); // CONSTRUCTEUR
…
};
L'allocation dynamique est réalisée en C++ avec les mots new et delete.
Elle permet de manipuler des pointeurs d'objets (utile entre autre pour définir un tableau d'objets).
Par exemple, pour manipuler un pointeur d'homme et contrôler sa durée de vie indépendamment du
16
bloc:
Autre exemple :
int *toto; // déclaration du pointeur d'entiers
toto = new int; // demande d'allocation mémoire pour une variable
// dynamique de type int
*toto = 10;
delete toto;
float *pt;
pt = new float[10];
for (int i=0 ; i<10 ; i++)
{
*(pt+i) = i * 2;
cout << *(pt+i);
}
delete [ ] pt;
Important : lorsqu'on transmet un objet par valeur en paramètre d'une fonction, le C++ lance un
constructeur appelé constructeur de recopie qui existe implicitement. Si l'objet contient des données
sous forme dynamique (tableaux), il va y avoir une mauvaise recopie. Il est dans ce cas indispensable
d'écrire soi-même le constructeur de recopie.
Par exemple, si la classe homme contient un tableau d'entiers (tableau de notes), il est indispensable
d'écrire un constructeur de recopie :
class homme
{
private :
...
int *notes; // tableau de notes
public :
homme (int , float); // constructeur
homme (const homme&); // constructeur de recopie
...
};
17
age = i;
taille = a;
notes = new int [10];
for (int j=0; j<10 ;j++) notes[j] = 10;
}
Equivalence d’écriture :
Si on a un pointeur sur un objet :
homme *toto ;
alors au lieu d’écrire :
(*toto).vieillir( ) ;
on pourra écrire :
toto -> vieillir( ) ;
Il peut se poser dans certains cas un problème de symétrie. Par exemple, si on possède une méthode
appelée mariage :
void mariage (homme);
L'appel de cette méthode peut être :
[Link] (berthe);
Mais l'appel suivant conduit au même résultat :
[Link] (robert);
Le problème de manque de symétrie vient du fait qu'une méthode est obligatoirement appliquée à un
objet. On pourrait contourner ceci en écrivant une fonction :
void mariage (homme,homme);
L'inconvénient est que la fonction n'a pas accès aux données placées en private (seules les méthodes y
ont accès).
Il suffit de placer devant la fonction le mot friend pour en faire un fonction amie de la classe et ainsi
elle aura accès à toutes les données de la classe :
friend void mariage (homme,homme);
Remarque : une fonction peut être amie de plusieurs classes.
18
public :
complexe (float =0.f, float =0.f);
friend complexe operator + (complexe, complexe);
complexe &operator ++ (void) ;
};
Un patron de classe (ou classe paramétrée) permet d'écrire une classe pour un type d'objets inconnus.
Par exemple une classe homme où on ne connaît pas la nature du tableau de notes (int, float, ...):
template <class toto> class homme
19
{
private :
toto *notes;
int age;
...
public :
...
void vieillir(void);
};
Manipuler et créer des objets, c'est faire de la Programmation Objet. L'Orientation Objet consiste à
créer une hiérarchie dans les classes avec une ou plusieurs classes de base et des classes dérivées qui
héritent de ces classes de base. L'intérêt est d'éviter de réécrire ce qui a déjà été écrit.
20
On n'a pas besoin de redéfinir toutes les caractéristiques d'une particule colorée et comment la
déplacer car elle hérite de toutes les propriétés de particule.
Remarque : Une classe dérivée a accès à toutes les méthodes publiques de la classe de base mais pas
aux méthodes privées. Si on veut que des données ou des méthodes soient accessibles aux classes
dérivées tout en restant inaccessibles aux autres classes et aux utilisateurs, on les précède non pas du
mot private mais du mot protected.
class particule
{
protected :
float x,y; // accessible par la classe particule et
// les classe dérivées
private :
float masse, charge; // accessible par la classe particule uniquement
public :
...
}
Héritage privé :
Si au lieu de l’héritage public :
class particulecoloree : public particule
on écrit l’héritage privé :
class particulecoloree : private particule
alors la classe dérivée n’hérite que des données de la classe de base et pas des méthodes. Ces
méthodes restent applicables aux objets de la classe de base mais pas aux objets de la classe dérivée.
Important : pour savoir si on doit faire ou pas de l’héritage avec une classe, il suffit de se dire que si la
classe contient la classe de base, alors ce n’est pas un héritage. Par contre si elle est la classe de base,
alors c’est de l’héritage.
* Méthodes virtuelles :
Quand on utilise de pointeurs sur des objets dérivant d’une classe de base, l’utilisation du mot-clé
virtual peut éviter certains problèmes. Par exemple si on déclare dans la classe de base particule la
méthode affiche en virtual :
virtual void affiche (void) ;
void particule::affiche(void)
{ cout << "Coordonnes" << x << " " << y << "\n " ; }
et si dans la classe particule coloree on déclare aussi une méthode affiche :
void affiche (void) ;
void particulecoloree::affiche(void)
{ cout << "Coordonnes" << x << " " << y << "\n " ;
cout << "Couleur" << couleur << "\n " ; }
alors on pourra écrire si adrpart est un pointeur de particule et adrpartcol est un pointeur de particule
21
colorée :
adrpart -> affiche( ) ; // affiche les coordonnées de la particule
adrpartcol -> affiche( ) ; // affiche les coordonnées et la couleur de la particule colorée
ce qui fonctionne même sans déclarer affiche en méthode virtuelle mais si on écrit :
adrpart = adrpartcol ;
adrpart -> affiche( ) ; // affiche les coordonnées et la couleur de la particule colorée
cela fonctionne car l’appel à la méthode est résolu au moment de l’exécution (c’est ce qu’on appelle le
typage dynamique) alors que sans le mot-clé virtual il ne serait fait appel qu’à la méthode affiche de la
classe particule.
* Classes virtuelles :
Quand une classe dérivée dérive de 2 classes qui elles-mêmes dérivent d’une classe de base commune
alors la classe dérivée contient 2 fois la classe de base. Pour lever toute ambiguïté, il faut déclarer la
classe de base virtuelle dans la déclaration de la classe dérivée :
Class derivee : virtual base
Références :
Langage C et Langage C++, Cours de Michel DUBUIT, 1997, CIRIL, Nancy.
Le langage C++, Bjarne STROUSTRUP, Addison-Wesley.
Exercices en langage C++, Claude DELANNOY, Eyrolles.
22
Exercices d'application
2 - Boucles.
Ecrire un programme affichant tous les entiers de 1 à n (n sera demandé par le programme) puis afficher la somme
de ces n entiers (ne pas utiliser un tableau).
4 - Tableaux.
Ecrire un programme qui vous demande d’entrer des notes entières au clavier. Entrer 99 signifie que la liste est
terminée. Stockez ces notes dans un tableau puis calculez et affichez la moyenne.
- Pointeurs.
Reprendre le même programme en utilisant un pointeur pour manipuler les données du tableau.
7 - Classe.
Ecrire une classe d’objets : homme, permettant de créer, de ‘détruire’, de faire vieillir, grandir, déplacer ,… un ou
plusieurs hommes en même temps. A vous d’imaginer et d’écrire ….
8 - Classe.
a - Construire une classe nommée : etudiant. Un étudiant sera défini par son âge et sa filière (codée à l’aide d’un
entier : 1=1ère année, 2=2ème année, 3=licence , …). La classe devra contenir un constructeur avec des paramètres par
défaut et un destructeur. Ecrire un programme testant cette question.
b - Ajouter à la classe etudiant des méthodes permettant de connaître l’âge et la filière de l’étudiant. Ajouter en
private une méthode permettant de vieillir l’étudiant d’un an et en public deux autres permettant de le faire passer
dans l’année supérieure ou de le faire redoubler et utilisant la méthode vieillir. Ecrire un programme testant cette
question.
c - Ajouter à la définition de l’étudiant la liste de ses 10 notes sous forme d’un tableau de flottants. N’oubliez rien :
le destructeur, la remise à zéro des notes pour le passage dans la filière supérieure , … Ecrire des méthodes
manipulant ce tableau (entrée de notes, sortie de notes, modification, …) dont une au moins aura en paramètre un
tableau de flottants et qui permettra de remplir le tableau de notes depuis un tableau du programme principal. Ecrire
un programme testant cette question.
d -Ajouter une autre méthode de passage à l’année supérieure en testant si la moyenne est supérieure à 10. Ecrire un
programme testant cette question.
23
e -Améliorer cette classe en ajoutant d’autres méthodes de votre choix …..
9 - Fonction amie.
a - Construire une classe nommée : point. Un point sera défini par deux coordonnées réelles. La classe devra
contenir un constructeur avec des paramètres par défaut et un destructeur.
Ecrire une méthode appelée : idem qui admet un point en paramètre et qui compare si le point courant est le même
que celui fourni en paramètre. Ecrire un programme testant cette question.
b - Ajouter à la classe point une méthode appelée : dist permettant de calculer la distance entre le point et l’origine.
Ecrire une fonction amie de la classe point appelée : meme_point qui fera la même chose que la méthode idem mais
avec la symétrie de l’appel en plus.
Ecrire une fonction amie de la classe point appelée : distance calculant al distance entre deux points. Ecrire un
programme testant cette question.
c - Construire une classe nommée : cercle. Un cercle est défini par le centre (un point de la classe précédente) et le
rayon. Ajouter des fonctions amies de la classe cercle qui comparent si le centre est le même, si le cercle est le
même, qui calcule la distance entre deux centres de deux cercles et si un cercle est inscrit dans un autre. Ecrire un
programme testant cette question.
b- Ecrire une classe nommée : complexe que vous utiliserez pour calculer l’impédance complexe dans un circuit
RLC série ou parallèle. Ecrire un programme testant cette question.
11 - Héritage de classe.
a- Ecrire une classe nommée particule ayant en private la position x d’une particule et sa masse m. En plus d’un ou
plusieurs constructeurs, faire une méthode qui déplace la particule sous l’action d’une force constante F passée en
paramètre. Pour cela, utiliser la loi du mouvement :
F 2
x= t +x 0
m
où x0 est la position précédente de la particule et t le temps. Ecrire un programme testant cette question.
b- Ecrire une classe particule_chargee (sans accent) qui hérite de la classe particule et qui ajoute comme donnée la
charge q de la particule. Ecrire une méthode permettant de retourner le rapport q/m (sans modifier la structure de la
classe particule). Ecrire un programme testant cette question.
c- Même question que précédemment en mettant la position et la masse de la particule non pas en private mais en
protected. Ecrire un programme testant cette question.
d- Ajouter des méthodes de votre choix dans la classe de base et la classe dérivée.
24