Vous êtes sur la page 1sur 118

Cours de Programmation

Introduction
C++
Laurent Henocque
Matre de Confrences

Ecole Suprieure dIngnieurs de Luminy

Universit de la Mditerrane Aix-Marseille II

Cours de programmation

Laurent Henocque

Le langage C++

Cours de programmation
Introduction C++
Utilisation avance du langage
Manuel de rfrence sommaire

ESIL / ES2I
Universit de la Mditerrane
par Laurent Henocque
(henocque@esil.univ-mrs.fr)
(http://www.esil.univ-mrs.fr/Staff/Prof/henocque/)

version 1.2 en date du 8 octobre 1996

Ce document peut tre librement reproduit dans son intgralit pourvu que la prsente mention de copyright ainsi que
celles prsentes en tte et en pied de page y restent attaches. Toute autre forme de copie est interdite.
Auteur Laurent Henocque, matre de confrences.
Ce document est un cours de l
Ecole Suprieure dIngnieurs de Luminy,
dpartement Etudes Suprieures en Ingnierie Informatique,
163 Avenue de Luminy, case 925, 13288 Marseille Cedex 9
(http://www.esil.univ-mrs.fr)

0.

Introduction et plan

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

Cours de programmation

Laurent Henocque

Le langage C++

Ce cours voque les notions fondamentales du langages C++ telles quexposes dans louvrage C++ Seconde Edition,
par lauteur du langage C++ : Bjarne Stroustrup. Ce livre constitue un document de rfrence irremplaable. Nous en
reprenons ici de nombreux exemples, et une partie de la structure.
Lobjectif du cours est de servir de rfrence pour ltudiant soucieux dexploiter le plus possible les qualits du langage,
et envisage donc les diffrentes constructions dans un assez grand dtail.
Voici le plan de ce cours :
1.
A LA DECOUVERTE DEC++ 1

2.

1.1.
introduction1
1.2.
paradigmes de programmation
1
1.3.
"Un meilleur C"
4
1.4.
C++ apporte un support pour les abstractions de donn
es
6
1.5.
C++ apporte un support pour la programmation par objets 9
FONCTIONS ET FICHIERS
12

3.

2.1.
introduction12
2.2.
porte d'dition
12
2.3.
porte d'dition 12
2.4.
fichiers d' en tte .h
13
2.5.
ditions de liens avec du code non C++
2.6.
comment construire les bibliothques15
2.7.
les fonctions 15
2.8.
macros
21
2.9.
macros 21
2.10.
exercices
24
CLASSES 26

4.

3.1.
classes et membres 26
3.2.
interfaces et mise en oeuvre 29
3.3.
autres caractristiques des classes
3.4.
construction et destruction 34
3.5.
exercices
36
3.6.
37
HERITAGE 38

5.

4.1.
introduction et tour d'horizon
4.2.
hritage : classes drives 38
4.3.
classes abstraites
38
4.4.
hritage multiple
39
4.5.
hritage virtuel
39
4.6.
contrle d'accs
39
4.7.
mmoire dynamique39
DECLARATIONS ET CONSTANTES
41
5.1.
5.2.
5.3.
5.4.

14

30

38

dclarations 41
les noms
43
les types
43
les littraux 50

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

Cours de programmation

Laurent Henocque

Le langage C++

6.

5.5.
les constantes nommes
51
5.6.
les champs de bits et unions 53
5.7.
exercices
54
EXPRESSIONS ET INSTRUCTIONS
56

7.

6.1.
sommaire des oprateurs
56
6.2.
les instructions
65
6.3.
commentaires et indentation 67
6.4.
exercices
67
SURCHARGE D'OPERATEUR
70

8.

7.1.
introduction70
7.2.
fonctions oprateurs 70
7.3.
conversions de types dfinis par le programmeur
7.4.
littraux
73
7.5.
gros objets 73
7.6.
affectation et initialisation 73
7.7.
indexation 73
7.8.
oprateur dappel de fonction
73
7.9.
indirections 73
7.10.
incrmentation et dcrmentation 74
7.11.
une classe chane de caractres
74
7.12.
amies et membres 75
7.13.
avertissement
76
TEMPLATES 77

9.

8.1.
introduction77
8.2.
un patron simple : patron de classe 77
8.3.
listes gnriques
77
8.4.
fonctions gnriques globales81
8.5.
rsolution de la surcharge des fonctions gnriques 82
8.6.
arguments de template
82
8.7.
drivation et templates
83
8.8.
un tableau associatif 83
GESTION DES EXCEPTIONS
85

10.

9.1.
9.2.
9.3.
9.4.
9.5.
9.6.
9.7.
9.8.
FLOTS

gestion des erreurs 85


discrimination et nommage des exceptions
acquisition de ressources
87
exceptions qui ne sont pas des erreurs89
spcification d'interface
89
exceptions non interceptes 90
alternatives la gestion des erreurs 90
90
91

10.1.
10.2.

introduction91
sorties
91

71

85

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

Cours de programmation

11.

Laurent Henocque

10.3.
entres
91
10.4.
mise en forme
93
10.5.
fichiers et flots
98
10.6.
entres/sorties C
99
NOTES RAPIDES AU PROGRAMMEURC
11.1.
11.2.

Le langage C++

100

rgles empiriques de base pour la conception d'objets100


notes aux programmeurs C 100

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

Cours de programmation

1.

Laurent Henocque

Le langage C++

A la dcouverte de C++
1.1. introduction
C++ est une tentative d'amliorer C
C++ supporte labstraction de donnes
C++ permet la programmation objet

1.2. paradigmes de programmation


1.2.1. la programmationprocdurale : algorithmie
choisir les procdures, implanter les meilleurs algorithmes
double sqrt(double) {
// calcule result avec le meilleur algorithme possible
return result;
}

1.2.2. la programmation modulaire


choisir les modules, masquer les donnes (exemple de la pile)
#include "stack.h"
static char v[stack_size]
static char * p=v;
void push(char c){/*vrification de dbordement et empilement*/}
char pop(){/*vrification de taille 0 et pop*/}

avantages : l'utilisateur ne connat que les fonctions, et la reprsentation interne peut changer
C++ supporte ce type de programmation par compatibilit avec C, et l'tend grce aux classes

1.2.3. l'abstraction de donnesdans un langage classique (C)


pour chaque type de donne, on donne un ensemble complet d'oprations : notamment des fonctions de construction et
de destruction et de copie, comme il en existe pour les types fondamentaux, mais aussi toutes les fonctions correspondant aux
services offerts par la classe
c'est ncessaire par exemple si l'on doit pouvoir utiliser deux piles simultanment, ce qui n'est pas permis par la
programmation modulaire ( cause de l'utilisation de variables globales)
Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

Cours de programmation

Laurent Henocque

Le langage C++

/*class*/ struct stack_id {};


stack_id create_stack();
void delete_stack(stack_id);
void push (stack_id, char);
char pop (stack_id);

les types ainsi conus par des classessont diffrents des types pr dfinisdans leur utilisation
le compilateur (C) reste incapable de dtecter certaines erreurs. Dans l'exemple ci dessous, s2 est utilis et dtruit sans
avoir t initialis.
main(){
stack_id s1,s2;
s1 = create_stack();
push (s1,'a');
char c = pop(s1);
push (s2,'b');
delete_stack(s2);
}

1.2.4. ... abstraction de donnes


C++ amliore cette situation. On dispose du mot clef "class". La classe est une unit d'encapsulation pour les fonctions
qui implmentent les services rendus, et pour les donnes ncessaires. Le langage interdit d'utiliser un objet non initialis, et
le compilateur devient capable d'effectuer certains contrles.
class complex {
double re,im;
public:
complex (double r, double im){re = r; im = i;}
complex (double r){re = r; im = 0;}// conversion
friend complex operator+(complex,complex);
friend complex operator-(complex,complex);
friend complex operator-(complex);
friend complex operator*(complex,complex);
friend complex operator/(complex,complex);

};
complex operator+(complex c1, complex c2){
return complex(a1.re+a2.re,a1.im+a2.im);
}
void main(){
complex a = 2.3;

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

Cours de programmation

Laurent Henocque

Le langage C++

complex b = 1/a;
complex c = a+b*complex(5,7.3);
c = c-a*b+2;
}

Dans l'exemple ci dessus, les fonctions de construction de la classe "complex" sont appeles automatiquement, et la
dfinition des oprateurs pour ce nouveau type permet de l'utiliser "comme" un type prdfini. C'est du C++.

1.2.5. difficults lies l'abstraction (quand on n'a pas de notion d'objet!)


il peut tre impossible d'adapter un type pour de nouvelles utilisations sauf en modifiant sa dfinition. En
programmtion classique, on doit souvent recourir des tests (au moyen de "switch" par exemple) que C++ rend inutiles,
facilitant ainsi considrablement la rutilisabilit des programmes.
class point {};
enum shapeType {circle, triangle, square};
class shape{
point center;
shapeType type;
public :
point where (){return center;}
void draw();
void rotate(float);

};
void shape::draw(){
switch (type){
case circle : break;
case triangle : break;
case square : break;
}
}

dans l'exemple ci dessus, chaque nouvelle forme ajoute impose de toucher le code de toutes les fonctions importantes
dfinies pour le type "forme"

1.2.6. la programmation par objets


le problme rencontr auparavant vient de ce que le langage ne permet pas de distinguer entre les proprits gnrales
des "shape" et cellesqui sont spcifiques de chaque sorte de "shape"
l'hritage permet de rsoudre ce problme

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

Cours de programmation

Laurent Henocque

Le langage C++

class shape {
point center;
public:
point where () { return center;}
void

move (point to) {undraw(); center = point; draw();}

virtual void draw();


virtual void rotate(float);
};

les fonctions dont l'interface d'appel peut tre dclare, mais ne peut pas tre dfinie, ont t dclares
virtual
de telles fonctions sont dites constituer le protocole de la classe abstraite shape.
une fonction virtuelle est lie dynamiquement : la bonne fonction est appele automatiquement sur un objet, mme
quand son type exact est inconnu
shape *tab[50];
void draw_all(float){
for (int i=0; i<50;i++){ tab[i]->rotate();}
}

1.2.7. la programmation par objets


chaque forme particulire est alors dclare comme une forme (via l'hritage, dclar par la syntaxe ":" ci dessous),
dont les caractristiques spcifiques peuvent tre prcises
class circle : public shape {
float radius;
public:
void draw(){};
void rotate(float){/*rien a faire*/}
};

le processus de dveloppement consiste alors


choisir les classes,
partager par hritage ce qui est commun,
implanter les fonctions spcifiques chacun des types

1.3. Comment C++ amliore le langage C


C++ apporte fonctions, arithmtique, instructions de branchement et constructions itratives
comme en C
les entres sorties sont accessibles au travers d'une bibliothque
, diffrente de celle connue en C
les constructions lmentaires etles pointeurs sont issus de C

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

Cours de programmation

Laurent Henocque

Le langage C++

la compilation spare apporte une notion minimale de module


, comme en C
on ne dispose toujours pas d'une notion de package qui fournirait un espace de nommage isol

1.3.1. le plus petit programme C++du monde


int main(){return 0;}

1.3.2. le programme "hello world"


#include <iostream.h>
int main(){
cout << "bonjour tout le monde\n";
return 0;
}

1.3.3. les entres sorties


#include <iostream.h>
int a;
cin >> a; // charge dans la variable a l'entier qui est frapp au clavier
cout << 2*a << endl; // affiche sur la console le double de a, et fin de ligne

1.3.4. variables et arithmtique


types fondamentaux : char, short, int, long, float, double, long double
oprateurs arithmtiques : + - * / %
oprateurs de comparaison : == != < > <= >=
C++ permet la conversion automatique entre les types de base, garantie sans perte d'information si possible (c'est
assur dl lors que le type origine est converti en un type dont la reprsentation est au moins aussi grande en nombre
d'octets).
double d;
int i = 2.0;//float vers int
short s;
d = d+i;//int vers double
i=s*i;//short vers int

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

Cours de programmation

Laurent Henocque

Le langage C++

1.3.5. pointeurs et tableaux


les types X tab[] et X* tab sont comparables de mme qu'en C (un tableau n'est jamais copi). Un tableau est
strictement identique un pointeur vers son premier lment.
char v[20]; // un pointeur vers 20 caractres conscutifs
char *p, *q;
q=v;

// deux pointeurs vers un caractre

// ok

p=&v[3];

// l'adresse du quatrime char est un pointeur

1.3.6. tests et itrations


if, switch, while, for
comme en C
on peut dclarer une variable au moment de sa premire utilisation, aprs des instructions, mme dans une instruction
"for" : for (int i=8; i>= 0; i--){}
la norme C++ prvoitmaintenant que la variable ainsi dclare soit locale la boucle, mais attention, de nombreux
compilateurs ne sont pas encore la norme.

1.3.7. fonctions
les types des arguments sont contrls lors de l'appel et ventuellement convertis
dans le type attendu
la signature d'une fonction (on dit aussi son "prototype") est constitue de :
son nom
les types de ses arguments dans l'ordre ou ils se prsentent
le type retourn par la fonction n'est pas indiffrent, mais n'est pas traditionnellement en C++ considr dans sa
signature.
deux fonctions diffrentes peuvent porter le mme nom : int f(int); int f(float). On parle alors de surcharge, ou de
polymorphisme. Le compilateur a l'intelligence de savoir quelle fonction appeler en fonction des types des arguments qui
sont proposs.
le passage d'argument par rfrence est possible comme en Pascal (passage VAR). En C, le mme mcanisme requiert
l'utilisation explicite de pointeurs.
void swap (int& i, int&j){ // permute les valeurs de deux variables
int aux = j;
j = i;
i = aux;
}
int main (){
int a = 1;
int b = 2;
cout << a << " " << b << endl;
swap(a,b);
cout << a << " " << b << endl;
return 0;

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

Cours de programmation

Laurent Henocque

Le langage C++

1.3.8. la ralisation demodules et de bibliothques


C++ permet l'dition de liens avec des fonctions crites en d'autres langages
compilateur des conventions de passage des arguments

condition

d'informer

extern "C" double sqrt(double);


extern ostream cout;

extern permet de spcifier le type de convention de passage des arguments (fortran et pascal sont comme "C")
on peut rpartir la programmation dans des fichiers spars avec rfrences externes
// header.h
extern char *chaine;
// chainedef.cc
#include "header.h"
char *chaine = "hello world\n";
//chainedisp.cc
#include "header.h"
#include <iostream.h>
main(){
cout << chaine;
return 0;
}
//compilation

les deux fichiers .cc sont alors compils par la commande :


CC chainedef.cc chainedisp.cc -o prog

ou encore:
CC -c chainedef.cc #genere chainedef.o
CC -c chainedisp.cc #genere chainedisp.o
CC chainedef.o chanedisp.o -o prog

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

le

Cours de programmation

Laurent Henocque

Le langage C++

1.4. C++ apporte un support pour les abstractions de donnes


1.4.1. initialisation et destruction
les classes comportent des mthodes de construction d'objet, et de destruction
on veut viter de sparerl'allocation de l'espace requis pourun objet et son initialisation (source d'oublis facheux)
un constructeur est une fonction qui porte le m
eme nom que la classe. Il y en a toujours plusieurs
le destructeur porte le nom de la classeprcd du caractre "~" (tilde)
class vector{
int _s;
int *_t;
public:
vector (int s){ // cette fonction est un constructeur de vector
assert(s>0);
_s=s;
_t=new int [s];
}
vector ():_s(0),_t(0){} // un autre constructeur, qui initialise tout 0
~vector () {delete []t;} // cette fonction est LE destructeur de vector
int operator[](int i){return _t[i];}
};

Le compilateur appelle automatiquement les constructeurs correspondant aux arguments fournis en mme temps que la
dclaration, et les destructeurs des variables locales
main(){
vector v(5); // appelle le constructeur un argument
vector w = 8; // appelle egalement ce constructeur
vector x;

// appelle le constructeur sans argument.

...
} // appelle le destructeur de vector pour x, puis pour w puis pour v

1.4.2. affectation et initialisation


on doit galement contrler les oprations de copie, dans deux cas : l'initialisation d'un objet avec un autre du mme
type pour argument (on parle alors de construction par copie), ou l'affectation d'un objet sur un autre.
par dfaut, la copie C++ est a copie champ champ de la structure C, en gnral non satisfaisante
:
void main(){
vector v1(100);

//initialisation classique

vector v2 = v1;

//initialisation par copie

v1 = v2;

//copie simple lors d'affectation

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

Cours de programmation

Laurent Henocque

Le langage C++

la classe vector peut trecomplte comme suit :


class vector{

void operator=(const vector&);

// affectation

vector (const vector&);

// initialisation par copie

};

question : pourqoui la copie par dfaut des champs des objets n'est elle pas satisfaisante?
question : comment programmer ces deux fonctionsafin de remdier ce problme?

1.4.3. patrons
en C++, on peut galement dfinir des types gnriques, comme un "vecteur "de n'importe quoi". Dans l'exemple ci
dessous, le symbole T est un type paramtre pour le template VectorOf :
template<class T> class VectorOf {
T* v;
int sz;
public:
VectorOf (int s) { v = new T[sz = s]; }
};
void f() {
VectorOf<int> v1(100);
VectorOf<complex> v2(100);
}

1.4.4. gestion des exceptions


en C, les exceptions ne sont pas prises en charge par le langage. Habituellement on utilise setjump et longjump (des
fonctions de libc) pour transfrer rapidement le contrle d'execution d'un programme lors de situations exceptionnelles sans
avoir prvoir de codes d'erreur en valeurs de retour des fonctions.
C++ apporte des constructions spcifiques pour le traitement des exceptions :
on dclare un type quelconque (notre sempiternel vector par exemple), et une (ou plusieurs) classe d'exception pour ce
type : ici "range" qui sera leve lors de dpassement des bornes du tableau :
class vector {
class range {};
...};

on lance le contrle tout programme susceptible de traiterl' exception par l'intruction "throw"

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

Cours de programmation

Laurent Henocque

Le langage C++

int vector::operator[](int i) {
if (i<0 sz<=i) throw range();
return v[i];
}

on peut alors intercepter l'erreur dans un bloc spcialis de type "try {} catch{}"
void f(int i) {
try {
vector v[i];
v[i+1000] = 12; // exception assure
// programmes pouvant aussi provoquer une exception
} catch (vector::range){
error ("attention : bornes de tableau dpasses");
}
}

1.4.5. conversions de types


C++ est un langage trs fortement typ statiquement. Le dfaut d'un tel langage est que le compilateur refuse de
prendre des vessies pour des lanternes : on doit parfois convertir (on dit "caster") des pointeurs d'un type vers un autre. En
C++ le programmeur peut dfinir des convertisseurs pour ses objets dans d'autres types, ce qui peut viter ces fameux cast, et
mme programmer des conversions d'un type vers un autre par forcment trs proche (un objet converti en entier qui indique
son tat par exemple)
les convertisseurs peuvent tre appels explicitement ou laisss aux bons soins du compilateur quand il en est capable
(lors d'un appel de fonction notamment)
complex a = complex(1); // explicite
complex b = 1; // implicite
a = b + complex(2); // explicite
a = b + 2; // implicite

les convertisseurs permettent aussi de rduire la complexit des interfaces de programmation : une seule fonction
complex operator+ (complex,complex) traite trois situations ds lors qu'il existe une convertion de int vers
complex

1.4.6. C++ permet de multiplier les implantations


on peut dfinir une superclasse abstraite de plusieurs implantations distinctes du mme type
par exemple : une pile dclare de faon abstraite puis implante sous les deux formes de tableau et de liste
template <class type> class stack {

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

10

Cours de programmation

Laurent Henocque

Le langage C++

public:
virtual void push(T) = 0; // pure virtuelle
virtual T pop(void)

= 0; // pure virtuelle

};

on ne peut pas utiliser d'instances d'une classe abstraite


stack<vtt> S; // erreur
void f(stack<car>& sc, car mercedes){ //ok
sc.push(mercedes);
car aux = sc.pop();
}

1.4.7. C++ permet de multiplier les implantations


on peut dfinir plusieurs sous types de piles, l'interface ne change pas
ex : avec un tableau
template <class T> class astack : public stack<T>{
// un tableau
public:
astack(int s);
~astack();
void push(T);
T pop(void);
}

ex : avec une liste :


template <class T> class lstack : public stack<T>{
// une liste
public:
lstack(void);
~lstack();
void push(T);
T pop(void);
}

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

11

Cours de programmation

Laurent Henocque

Le langage C++

1.5. C++ apporte un support pour la programmation par objets


1.5.1. mcanismes d'appel
appel de fonction membre par les oprateurs -> et .
class shape{
virtual void rotate(int);
};
void main(){
shape *p;

p->rotate();
}

dtermination statique de la fonction membre


c'est le cas lorsqu'elle n'est pas dclare virtuelle : le compilateur choisit celle qui est dfinie pour la classe connue de
l'objet
dtermination du membre dynamique
lorsqu'elle est virtuelle : le compilateur appelle la fonction indirectement, au travers d'un champ appel vtbl
cet appel est aussi efficace qu'un appel normal de fonction

Shape
center
color

vtbl

Circle
vtbl

&?::draw()
&?::rotate()

&Circle::draw()
&Circle::rotate()

1.5.2. vrification de type


les types des arguments des fonctions sont contrls par le compilateur, ce qui permet une dtection prcoce des erreurs
la vrification statique associe au mcanisme de fonctions virtuelles est trs performante compare la vrification de
type dynamique de certains langages (Smalltalk)
les erreurs sont par ailleurs dtectes avant l'excution

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

12

Cours de programmation

Laurent Henocque

Le langage C++

ex smalltalk:
Stack s; // pile fourre tout
void main(){
s.push (new Peugeot(309));
s.push (new Boeing(727));
s.pop()->takeoff();
s.pop()->takeoff(); // erreur excution : une 309 ne vole pas

ex C++:
Stack s<plane>; // pile d'avions
void main(){
s.push (new Boeing(727)); // ok
s.push (new Peugeot(309)); // erreur compilation
s.pop()->takeoff();

Smalltalk permet de dfinir pour une classe un ensemble minimal d'oprations, l'utilisateur tant libre d'(essayer d')
appeler une fonction non dfinie la base
C++ permet de dfinir une interfaceexacte, toute erreur tant dtecte ds la compilation

1.5.3. hritage multiple


C++ permet de dfinir des classes qui hritent de plusieurs super classes
ce mcanisme est prcieux car il offre une flexibilit qui ne peut tre atteinte sinon
class task {};
class printableObj {};
class taskPrintable : public task, public printableObj{};
class mytask : public task {};
class myprintable : public printable {};
class mytaskPrintable : public taskPrintable {};

sans l'hritage multiple, deux seulement des trois choixci dessus sont accessibles
C++ ne tient aucun compte de l'ordre de l'hritage pour rsoudre les ambiguts, et demande une dsambiguation
explicite

1.5.4. un exemple d'hritage multiple


class A {void f();};
class B {void f();};
class C : public A, public B {/*f non redfini*/};
class D : public A, public B {
void f() {
A::f(); // appel explicite de f pour la classe A;

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

13

Cours de programmation

Laurent Henocque

Le langage C++

B::f(); // appel explicite de f pour la classe B


}
};
main(){
C c;
c.f();// erreur : ambigu
c.A::f(); // OK
D d;
D.f();// ok
}

1.5.5. l'encapsulation
les fonctions oprant sur un objet sont encapsules dans la classe.
l'unit d'encapsulation est la classe, et non l'objet : un objet d'une classe possde tous les droits sur un autre objet de la
mme classe
en particulier, une classe C++ n'est pas un objet C++ : la classe n'est pas une entit palpable, mais une abstraction utile
seulement lors de la compilation
C++ permet de restreindre l'accs aux membres des classes, donnes ou fonctions, sans distinguer entre les
autorisations de lecture et d'criture pour les donnes membres
public donne l'accs tout programme
private interdit l'accs tout programme, y comprisun membre d'une classequi en hrite
protected donne l'accs aux seuls membres des (futures) sous classes
le mot clef friend permet de donner l'accs de faon explicite (toutes les fonctions d') une classe, o une fonction
dsigne explicitement

et en criture pour les champs

l'accs une fois possible l'est en lecture

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

14

Cours de programmation

2.

Laurent Henocque

Le langage C++

Fonctions et fichiers
2.1. introduction
le fichier a un rle unique : dfinir la porte de fichier pour :
les fonctions static,
les fonctions inline,
les variables globalesstatic et
les variables globalesconst
cest aussi habituellement une unit de stockage et de compilation

2.2. La porte d'dition


un nom qui nest pas local une fonction ou une classe doit dsigner un objet du mme type dans tous les fichiers
dun programme compilen plusieurs units
donc un nom de fonction ou de variable n'apparat qu'une seule fois dans un programme (on entend ici programme
excutable)
// fic1.cc
int a = 1;
int f() {/* du code ici */}
//fic2.cc
extern int a;

// la variable a de fic1.cc

int f();

// la fonction f de fic1.cc

void g() { a = f();}

// g dfinie ici appelle f dfini dans fic1.cc

un objet doit tre dfini une seule foisdans un programme


il peut tre dclar plusieurs fois, de faon identique, notamment dans diffrents fichiers combins pour produire le
programme excutable
// fic1.cc
int a = 1;

// dclaration ET dfinition

int b = 1;

// id

extern int c;

// dclaration seule

//fic2.cc
int a;

// erreur, signifie int a = 0; les globales sontinitialises

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

15

Cours de programmation

Laurent Henocque

Le langage C++

extern double b;

// erreur type diffrent

extern int c;

// erreur, dclaration mais pas de dfinition

2.3. porte d'dition


on peut faire empcher la publication d'un symbole dans tout le programme par une dclaration interne explicite
utilisant static (porte de fichier)
la porte static facilite la comprhension des programmes
les noms de classe doivent tre uniques tout le programme,et rfrer des descriptions identiques
// fic1.cc
struct S {int a; char b;};
extern int f(S*);
//fic2.cc
struct S {char a; int b;}; // diffre de S dans fic1
int f(S*p){/*your code here*/}
// (certains compilateurs ne verront pas lerreur)

habituellement, les dfinitions de classes sont dcrites dans des fichiers d'en tte inclus par tous les fichiers qui les
utilisent
les fonctions inline ont une porte ddition interne
les constantes (const) aussi
les typedef sont locaux leur fichier galement. Attention, en C++ typedef ne sert pas dfinir un type mais dfinir
un nouveau nom pour un type, ou un nom de type composite.
on a la possibilit dedclarer des constantes externes explicites
extern const int a;
const int a = 1;

2.4. fichiers d' en tte .h


leur suffixe est habituellement .h
ils sont inclus dans les fichiers sources .cc
#include monfic.h // cherche d'abord dans le rpertoire courant puis comme <>
#include <libfic.h> // dans les rpertoires dinclude standard et utilisateur

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

16

Cours de programmation

Laurent Henocque

Le langage C++

2.4.1. contenu normal d'un fichier .h


dfinitions de types

struct list{};

patrons

template <class T> class A {T f();};

dclarations de fonctions

extern void g();

dclarations de fonctions inline

inline max(int a,int b) {}

dclarations de variables

extern char calu;

dclarations de constantes

const int MAXINT = 10000;

dclarations d'numrations

enum codes {NUL, BCD, WKL}

dclarations de noms

struct list;

inclusions

#include

dfinitions de macros

#define ONDEBUG(prog) {prog}

commentaires

// et /* */

2.4.2. ce quil ne faut pas y mettre


dfinition de fonctions normales,

void g(){};

dfinition de donnes

int maglobale; //initialise

dfinition d'agrgats de constantes

const tab[] = {1,3,5};

2.4.3. le projet fichier en tte unique


on dfinit un unique fichier .h pour un projet, qui permet la dclaration des lments communs tous les fichiers
compiler
utile sur un petit projet que l'on veut compiler par parties

2.4.4. le projet fichiers en tte multiples


utile lorsque des parties peuvent tre utilises sparment
ncessaire sur les grands projets pour viter que tous les programmes soient compils avec toutes les dclarations, ce
qui ralentit la compilation
on dcrit souvent linterface publique de programmation dun fichier .c dans un .h de mme nom

2.5. ditions de liens avec du code non C++


2.5.1. extern "C" {}
sert dire au compilateur que les conventions d'dition de liens sont celle de "C" pour un ensemble de fonctions ou de
variables
n'affecte pas la porte, la smantique, ni les ordres d'appels

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

17

Cours de programmation

Laurent Henocque

Le langage C++

fortran et assembleur ont les mmes conventions que C


possibilit d'utiliser le symbole __cplusplus dfini par le
prprocesseur du compilateur
extern C char * strcpy(char *, const chat *);

ou encore
extern C {
char * strcpy(char *, const char *);
char * strcmp(const char *, const char *);

2.5.2. extern "C" {}


pour utiliser un fichier de dclarations dune bibliothquestandard C :
extern C {
#include <string.h>
}

pour crire un fichier .h compatible entre C et C++:


#ifdef __cplusplus
extern C {
#endif
char * strcpy(char *, const char *);
char * strcmp(const char *, const char *);

#ifdef __cplusplus
}
#endif

Note : la directive extern C spcifie la convention ddition de liens, mais le programme compil doit rester du C++,
qui est plus rigoureux que C, notamment sur lutilisation effective des arguments des fonctions

2.6. comment construire les bibliothques


2.6.1. gnration d'objets
CC -c prog.cc

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

18

Cours de programmation

Laurent Henocque

Le langage C++

2.6.2. construction de bibliothques


ar rv prog1.o, prog2.o malib.a
ranlib malib.a

2.6.3. compilation d'excutables


CC main.cc -o main malib.a

2.7. les fonctions


pas d'appel possible avant la dclaration(en C, une fonction non dclare tait sense retourner un int).

2.7.1. dclarations
une dclaration de fonction mentionne les types de la valeur de retour et des arguments et le nom de la fonction
une fonction est soit globale (comme une fonction C) soit membre d'une classe donne. On y accde alors avec les
oprateurs . et -> comme dans le cas des donnes membres.
class StackOfInt {
// ...
void push

(int data);

int pop ();


};
// ...
main(){
StackOfInt s;
s.push(5);
(&s)->pop();
}

le passage d'argument est comparable linitialisation : les types sont vrifis et les conversions ncessaires qui sont
possibles sont ralises
les arguments peuvent tre nomms lors de la dclaration : cela rend les fichiers .h plus lisibles, car le nom d'un
argument claire souvent plus que son type sur son rle.
on peut fournir des valeurs par dfaut aux derniers arguments fournis dans la dclaration. Ces arguments sont alors
optionnels.
int fonction (int a, char* s = "", float f=2.0);
fonction(12); // ok : fonction(12,"",2.0);
fonction(12,"aze"); // ok : fonction(12,"aze",2.0);

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

19

Cours de programmation

Laurent Henocque

Le langage C++

fonction(12,3.4); // non : les arguments doivent tre renseigns dans l'ordre


fonction(12,"ert",5.6); // ok

on peut donner le type d'une fonction qui attend un nombre variable d'arguments.
int printf (char* format ...); // un char * puis autant d'arguments que voulu

2.7.2. dfinition de fonction


toute fonction appele doit tre dfinie quelque part dans le programme
avant son appel
une dfinition de fonction est une dclarationplus un corps entre accolades
int double (int a) {return 2*a;}

des arguments peuvent rester inutiliss :il suffit de ne pas les nommer pour obtenir le calme du compilateur
une fonction peut tre dclare inline
inline int double (int a) {return 2*a;}

dans ce cas, le compilateur insre le code (assembleur si on veut) de la fonction chaque appel, et sait parfois mme
faire mieux :
inline int fact (int n) {return n<2 ? 1 : n*fact(n-1);}
int i=fact(5); // gnre i=120;

un trs bon compilateur doit savoir calculer la valeur de i ci dessus la compilation

2.7.3. passage d'arguments


pour le passage d'arguments, de la mmoire est rserve dans la pile d'excution, et les arguments rels sont initialiss
par copie
C++ permet le passage par rfrence, utile pour le passage de gros objets. Un argument pass par rfrence est
comparable un argument VAR en Pascal. La rfrence s'emploie comme un paramtre copi, mais se comporte comme un
pointeur.
void f(const large& arg); // const permet que l'original ne soit pas modifi

il faut utiliser const autant que possible, mme pour les passages d'arguments par pointeur, lorsque la fonction ne doit
pas modifier l'objet point
int strlen (const char *); // strlen ne modifie pas le contenu de la chane

un littral, une constante, ou un argument qui demande une conversion peuvent tre passs comme argument d'une
fonction qui attend const&(une rfrence un objet const), mais pas d'une fonction qui attend une rfrence non const
float fortran_sqrt(const float&);
void f(double d){

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

20

Cours de programmation

Laurent Henocque

Le langage C++

float r;
fortran_sqrt(2.0f);

// rf tmp= 2.0f (erreur si non const)

fortran_sqrt(r);

// rf r, ok dans tous les cas

fortran_sqrt(d);

// rfrence tmp= float(d) (erreur si non const)

c'est normal car l'original pass en argument rfrence non const est susceptible d'tre modifi par la fonction appele.
Ce n'est pas possible pour la constante 2.0f ci dessus, ni pour la temporaire de conversion de "d" en float : la conversion
inverse n'existe pas.

2.7.4. valeur de retour


une fonction non void doit retourner une valeur, indique par return
une instruction de retour est considre comme initialisant une variable du type retourn
toutes les conversions licites peuvent avoir lieu dans ce cadre
double f(){

return 1; // converti en double(1)


}

on ne peut retourner ni une rfrence, ni un pointeur vers une variable locale ou une constante locale :
int * f(){
int aux;
return &aux;//erreur
}
int & f(){
int aux;
return aux;//erreur
}
int & f() {return 1;} // erreur

2.7.5. arguments de type tableaux


un tableau est pass comme pointeur vers son premier lment
un argument de typeT[] est converti en T*
un tableau ne peut pas tre pass par valeur (c'est le seul cas en C++)
la taille d'un tableau est inconnue pour la fonction appele
dans le cas des tableaux multidimensionnels, le compilateur a besoin de connatre les dimensions du tableau pour
gnrer le code d'indexation. Une seule dimension du tableau peut tre ignore, sauf implanter lsoi mme l'indexation (sans
perte de performance cela dit, voir exemple ci dessous).
void print_m34(int m[3][4]){ //cas idal

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

21

Cours de programmation

Laurent Henocque

Le langage C++

for (int i = 0 ; i<3 ; i++)


for (int j = 0 ; j<4 ; j++)
cout <<"lment i,j :" << m[i][j]<< endl;
}
void print_mi4(int m[][4], int dim1){ //cas moins idal mais OK
for (int i = 0 ; i<dim1 ; i++)
for (int j = 0 ; j<4 ; j++)
cout <<"lment i,j :" << m[i][j]<< endl;
}
void print_mij(int m[][], int dim1, int dim2); //impossible
void print_mij(int **m, int dim1, int dim2){ // la solution OK
for (int i = 0 ; i<dim1 ; i++)
for (int j = 0 ; j<dim2 ; j++)
cout <<

"elt i,j :" << ((int*)m)[i*dim2+j] << endl;

le code prcdent est le mme que celui gnr par le compilateur

2.7.6. surcharge de noms de fonctions


C++ permet de donner le mme nom des fonctions qui diffrent par leurs seuls arguments
la signature d'une fonction ne prend pas en compte le type de la valeur de retour
un appel de fonction donn choisit la fonction qui est dans la porte pour laquelle un ensemble de conversions existe
qui permettrait de l'appeler, est qui correspondle mieux possibleaux arguments rels.
la fonction choisie est celle qui dcrit une correspondance meilleure que tout autre pour au moins un argument. Dans
tout autre cas, il y a ambigut, et une erreur de compilation
void print(double);
void print(long);
f(){
print (1L);

//ok 1L est un int de type long (L)

print (1.0);

//ok 1.0 est une constante de type flottant

print (1);

//erreur, ambigu car les deux conversion existent

ou encore, de faon plus vicieuse :


void print(char *);
void print(long);
f(){
print (1L);

//ok

print ("toto");

//ok

print (0);

//erreur, ambigu : zro ou pointeur nul?

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

22

Cours de programmation

Laurent Henocque

Le langage C++

dans ce dernier cas, noter que zro est un int : lambigut nat ici de ce qu'il existe deux conversions possibles

2.7.7. surcharge de noms de fonctions


les fonctions n arguments optionnels sont considres comme n+1 fonctions
si un membre d'une classe X est explicitement appel sur un pointeur en utilisant l'oprateur ->, cet argument
supplmentaire est suppos avoir le type const X* pour les membres const, volatile X* pour les membres volatile et X* pour
les autres.
si un membre d'une classe X est explicitement appel sur un objet en utilisant l'oprateur ., ou si la fonction est
invoque sur le premier oprande d'un oprateur surcharg, cet argument supplmentaire est rput avoir le type const X&
pour les membres const, volatile X& pour les membres volatile et X& pour les autres.
aucune squence de conversions contenant plus de une conversion dfinie par l'utilisateur, ou pouvant tre raccourcie
n'est examine.
la squence de correspondance la plus courte pour construire d'un type un autre s'appelle squence de meilleure
correspondance
les points de suspension correspondent tout argument rel de n'importe quel type. Dans ce cas le compilateur
n'effectue pas de conversion des arguments. Toutefois les arguments fournis dont la taille est infrieure celle d'un int sont
convertis en int.

2.7.8. conversions triviales


Les conversions trivialesci dessous n'affectent pas laquelle de deux conversions est la meilleure.
T

T&

T&

T[]

T*

T(args)

T(*)(args)

const T

volatile T

T*

const T*

T*

volatile T*

2.7.9. les conversions standard


promotions d'entiers (automatique des petits entiers vers int ou unsigned suivant l'implantation)
conversions d'entiers (vers unsigned, ou vers un type sign plus petit)
float et double (l'un vers l'autre, avec des pertes)
float et entier (perte de la partie dcimale)
conversions arithmtiques (pour harmoniser les types de oprandes arithmtiques, vers le type du plus grand)
conversions de pointeurs : de 0 vers ptr, de prt non const ni volatile vers void*, de fonction vers void* si assez grand,
de classe vers classe de base, de tableau vers ptr, de fonction vers adresse de fonction (sauf adresse explicite ou appel)
conversions de rfrences : d'une classe vers classe de base accessible
Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

23

Cours de programmation

Laurent Henocque

Le langage C++

pointeurs sur membres : d'une classe vers une classe drive


Note : dans le cas des pointeurs sur membre, la conversion est inverse par rapport aux pointeurs normaux. Ce ne sont
pas de vrais pointeurs, et ils ne peuvent d'ailleurs pas tre convertis vers void *

2.7.10.

rgles de priorit pour les correspondances

1 : correspondance exacte
les squences de zro ou plus conversions triviales sont meilleures que toute autre squence.
celles qui ne convertissent pas vers const ou volatile sont meilleures que toutes les autres.
2 : correspondance avec les promotions
les squences qui ne contiennent que des promotions d'entiers, des conversions de float en double, et des triviales sont
meilleures que toutes les autres
3 : correspondance avec les conversions standard
les squences qui ne contiennent que des conversions standard et triviales sont meilleures que toutes les autres
4 : correspondance avec les conversions dfinies par l'utilisateur
5 : correspondance avec les points de suspension dans la dclaration
si deux correspondances existent au mme niveau de priorit, l'appel est ambigu et rejet

2.7.11.

arguments par dfaut

on peut spcifier des valeurs par dfaut aux arguments d'une fonction. L'argument absent lors d'un appel effectif sera
remplac par cette valeur
ce n'est possible que pour les derniers arguments de la fonction
cela doit tre fait la dfinition de la fonction
void print (int d , int base=10);
f(){
print (3);// print(3,10);
print (4,16);
}

on peut simuler ce mcanisme par surcharge (et l'tendre aux autres arguments)
void error (char *text, int code);
inline void error (int code) { error ("no text here", code);}

2.7.12.

arguments de nombre et type inconnus la dclaration

on utilise dans ce cas les points de suspension, qui permettent au compilateur d'accepter des arguments
supplmentaires, utiliss ou non par la fonction lors de l'appel
extern "C" int printf(const char * ...);
extern "C" int fprintf(FILE*, const char * ...);

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

24

Cours de programmation

Laurent Henocque

Le langage C++

extern "C" int execl(const char * ...);

le fichier stdarg.h dcrit des macros permettant de concevoir de telles fonctions


node * buildnode(node *first ...){
va_list ap;

//ap pour Argument Pointer

va_start(ap,first);

//initialise

node *child;

//auxiliaire

// on cree un noeud ici


for (child = first ; child != 0 ; child = va_arg(ap,node*)){
// bravo un fils on l'ajoute a son pre
// on sarrte zro
}
va_end(ap);

//ncessaire

}
f(){
node *n=buildnode(new node(), new node(), (node*)0);
}

noter que 0 est un int, et que sizeof int est potentiellement diffrent de sizeof (node *), do le cast

2.7.13.

pointeur de fonction

on peut soit appeler une fonction soit prendre son adresse


void error(char*);
void (*errpt) (char*);//une variable
typedef void (*errpttype) (char*);//un type pour ces variables
f(){
errpt = &error;
errpttype aux = &error;
(*errpt)("test");
(*aux)("test");
}

lors d'une affectation un pointeur de fonction, le type de l'argument doit correspondre exactement, y compris pour sa
valeur de retour
les rgles de l'appel direct s'appliquent aux pointeursde fonction sans changement
on peut dfinir un type pointeur de fonction pour rendre plus aise la manipulation (cf. signal.h)
ces pointeurs sont utiles pour :
paramtrer la dynamique d'un systme (actions correspondant des menus par exemple)

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

25

Cours de programmation

Laurent Henocque

Le langage C++

raliser des procdures polymorphiques (comme la fonction "map" de lisp) : on passe l'adresse d'une fonction en
paramtre une autre pour effectuer un traitement global (tri d'un tableau d'objets inconnus pour lequel on mentionne un
fonction de comparaison exemple de qsort dans libC)
on peut prendre l'adresse d'une fonction inline, ou d'une fonction surcharge

2.8. macros
elles sont d'un usage plus limit en C++ qu'en C, cause de "template", "inline", et "const"
elles sont parfois utiles
les compilateurs et dbuggers sont largus, parce que les macros gnrent du code sur une seule ligne, et le
prprocesseur est invoqu avant la compilation proprement dite
#define ma_macro du texte a qui mieux mieux
#define

le texte p enrob

donnent l'utilisation :
ma_macro fada
-> du texte a qui mieux mieux fada
avec_param(qui suit est)
-> le texte qui suit est enrob

partout ou C++ offre une construction quivalente il faut l'utiliser


const int MAXINT = 32768;
template <class type>
inline type max(type a, type b){return (a<b) ? b : a;}

les macros rcursives sontinterdites (tentation de factorielle)

2.9. macros
lorsqu'un argument d'une macro intervient dans une expression, la macro doit le parenthser
lorsqu'un argument d'une macro est une squence d'instructions, la macro doit le mettre entre accolades
dans une macro, utiliser les commentaires /* */ la C
les mentions de noms globaux dans une macro doivent tre prfixes de "::", afin de ne pas subir de masquage local
intempestif
lorsque le rsultat de la substitution est une squence d'instructions, la macro doit la mettre entre accolades.
Par exemple :
#define carre(a) a*a
#define fonc(prog) if () prog else exit(); validate();

devraient tre crites :

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

26

Cours de programmation

Laurent Henocque

Le langage C++

#define carre(a) (a)*(a)


#define fonc(prog) {if () {prog} else exit(); validate();}

2.9.1. tapes
le pr processeur ralise les oprations suivantes :
des caractres de passage la ligne sont introduits pour remplacer les indicateurs de fin de ligne dpendant du systme
(ainsi que toute autre traduction dpendant du systme). les squences trigraphes sont remplaces
(voir ci dessous)
la paire du caractre \ suivi d'un passage la ligne est efface
le source est dcompos en symboles de pr traitement et squences d'espaces. Chaque commentaire est remplac par un
unique espace
les directives sont excutes et les macros sont gnres
les squences d'chappement dans les chanes sont remplaces par leur quivalent
les littraux chaine adjacents sont concatns
la compilation peut enfin avoir lieu

2.9.2. squence trigraphe


les squences suivantes sont substitues avant toute chose
??=

??/

??'

??(

??)

??!

??<

??>

??-

2.9.3. oprateur #
suivi d'un paramtre de macro, les deux sont remplacs par la chaine de caractres fournie en argument rel de la
macro

2.9.4. oprateur ##
il permet de concatner un symbole et un paramtre de macro, en supprimant les espaces
cela permet de crer de nouveaux symboles

2.9.5. analyses successives


une chaine produite par une macro est ranalyse jusqu' ce que plus aucune substitution ne soit possible
une macro en cours de gnration ne peut pas tre gnre rcursivement (factorielle)

2.9.6. porte des noms de macros et #undef


une macro est active ds sa dfinition jusqu' la fin du fichier obtenu par tous les #include ou jusqu' la rencontre de
#undef

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

27

Cours de programmation

Laurent Henocque

Le langage C++

un #undef abusif est ignor

2.9.7. inclusion de fichiers


#include <nom> provoque une recherche de nom dans les rpertoires d'include standard et ceux spcifis par
lutilisateur
#include "nom" provoque la recherche de nom dans le rpertoire courant, et en cas d'chec continue comme pour
#include <nom>

2.9.8. compilation conditionnelle


#if expression-constante-entire
defined value 1 si le symbole est dfini, ou zro
#ifdef symb quivalent #if defined (symb)
#ifndef symb

quivalent #if ! defined (symb)

#else
#elsif

2.9.9. contrle des lignes


#line constante "nom_fichier"opt

cette directive modifie la valeur des macros pr dfinies__LINE__ et __FILE__

2.9.10.

directive d'erreur

#error chaine-symbole

provoque la gnration d'un message d'erreur

2.9.11.

pragmas

#pragma chaine-symbole

provoque un comportement dpendant de la mise en oeuvre (voir la doc du compilateur)


#pragma once fait enregistrer le nom du fichier de sorte qu'il ne sera plus rinclu dans le mme programme

2.9.12.

directive nulle

#
n'a aucun effet

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

28

Cours de programmation

2.9.13.

Laurent Henocque

Le langage C++

noms pr dfinis

__LINE__
__FILE__
__DATE__
__TIME__
__cplusplus
__STDC__ est dfini par certains compilateurs
les compilateurs dfinissent normalement de nombreux symboles qui permettent de rendre du code dpendant du
systme, de la machine, du niveau de respect du standard C++

2.10. exercices
2.10.1.

exercice

crire des dclarations de fonctions prenant des pointeurs de fonctions en arguments et retournant de tels pointeurs

2.10.2.

exercice

que signifie typedef int (&rifii)(int,int);

2.10.3.

exercice

crire un programme qui affiche Hello suivi de tous les arguments de la ligne de commande

2.10.4.

exercice

crire le programme cat, qui lit les fichiers nomms sur la ligne de commande et les crit sur sa sortie standard

2.10.5.

exercice

programmer une fonction de tri "sort" prenant une fonction de comparaison en argument

2.10.6.

exercice

dcrire une structure de terme acceptant plusieurs fils


crire une fonction qui construise un tel terme dont les fils sont passs en arguments, de sorte que l'arbre puisse tre
exprim presque comme s'il tait interprt

2.10.7.

exercice

programmer itoa

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

29

Cours de programmation

2.10.8.

Laurent Henocque

Le langage C++

exercice

lire les fichiers .h prsents dans /usr/include et /usr/include/CC

2.10.9.

exercice

crire une fonction qui inverse u tableau deux dimensions

2.10.10.

exercice

crire un programme de cryptage qui utilise une clef passe en paramtre dont les caractres sont combins de faon
itrative aux caractres du fichier traduit

2.10.11.

exercice

crire la fonction printf

2.10.12.

exercice

dfinir des rgles typographiques pour marquer la diffrence entre les diffrentes entits nommes par vos programmes
(fonctions, pointeurs vers, variables locales, globales, etc)

2.10.13.

exercice

crire un processeur de macros qui traite des macros simples

2.10.14.

exercice

crire un programme qui appelle une fonction (sqrt par exemple) de la bibliothque C standard (sans include <math.h>)

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

30

Cours de programmation

3.

Laurent Henocque

Le langage C++

Classes
3.1. classes et membres
les classes sont des types dfinis par l'utilisateur
aucun type de base de C++ n'est une classe

3.1.1. fonctions membres


une classe est une structure laquelle s'appliquent un certain nombre de fonctions
ces fonctions sont encapsules dans la classe, et ne peuvent s'appliquer qu' des objets instances de la classe
des structures distinctes peuvent avoir des fonctions membres de mme nom
struct individu {
char *nom;
int age;
void set (char *nom,int age);//une fonction membre
void print();//une autre
};

3.1.2. classes
le type "class" permet la restriction des appels : seules des fonctions "autorises" pourront invoquer une fonction
membre, ou bien agir sur un membre (lire o modifier)
cela permet une localisation plus facile des erreurs : tout tat erron est le fait des seules fonctions membres
class individu {
char *nom;
int age;
public:
void set (char *nom,int age);
void print();
};

3.1.3. auto rfrence


une fonction membre est invoque sur une instance d'une classe
un pointeur vers l'objet qui porte l'appel est pass implicitement en paramtre lors de l'appel
on peut obtenir ce pointeur avec la variable fictivethis

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

31

Cours de programmation

Laurent Henocque

Le langage C++

on ne peut pas assigner this, mais on peut modifier l'objet point. Pour une classe X, le type de this dans les fonctions
membres est :
X *const this;

utiliser this pour dsigner l'objet n'est pas ncessaire : les membres d'une classe sont accessibles directement par les
fonctions membres d'un objet
class X {
int _a;
void f(int a) {_a = a;} // signifie this->_a = a;
};

3.1.4. auto rfrence


quand on veut un membre ne pouvant pas modifier l'objet on mentionne
const aprs les arguments
class X {
int _a;
public :
int get_a() const {return _a;} //ne modifie pas l'objet point
};

un membre const peut tre invoqu sur un objet const, une fonction ordinaire ne peut pas
noter que le compilateur ne dtecte pas seul les tentatives de modification d'un objet const : il est essentiel de faire
figurer ce type dans la dclaration des fonctions
le type de this dansun membre const de la classe X est
const X *const

en cas de besoin, il y a possibilit de coercition explicite pour passer outre ce const :


void X::f() const { ((X*)this)->compteur++;}

C'est utile pour modifier physiquement un objet dont l'tat logique n'est cependant pas affect par l'opration : par
exemple pour implanter un compteur d'accs l'objet (cas ci dessus). On distingue la constance logique de la constance
physique.

3.1.5. initialisation
dans une classe X, on peut dfinir des fonctions membres d'initialisation qui portent le nom X
class individu {
char *
int

_nom;

_age;

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

32

Cours de programmation

Laurent Henocque

Le langage C++

individu();//pour initialiser un individu


};
individu::individu(char *nom, int age):_nom(nom),_age(age){}

lors de la cration d'objets d'une classe donne, on doit fournir les argument attendus par le constructeur utilis :
main(){
individu bob("bob",34);
individu *paulptr = new individu("paul",23);
}

un constructeur est invoqu dans les condition suivantes :


lors de la cration d'une variable automatique (le cas de bob ci dessus)
lors de la cration d'un objet allou sur le tas
pour passer un paramtre rel
pour passer une valeur retourne
lors d'une conversion dans le type correspondant

3.1.6. initialisation
on dfinit en gnral de multiples constructeurs, avec des arguments par dfaut
individu::individu(char *nom="", int age=0):_nom(nom),_age(age){}

un constructeur particulier est le constructeur de copie, de type :


X::X(const X&);

la copie par dfaut est une copie champ champ (chaque champ est copi avec son propre constructeur de copie s'il
existe
si aucun constructeur de copie n'existe, pour la classe comme pour les donnes membres, la copie par dfaut est celle
de la structure : recopie bit bit
on peut crer un tableau d'instances d'un classe condition que cette classe dfinisse un constructeur sans paramtre.
On ne peut pas passer de paramtre au constructeur pour initialiser tous les objets d'un tableau
Attention : un constructeur un seul argument peut tre appel automatiquement par le compilateur pour convertir un
argument lors d'un appel de fonction. Pour l'empcher, on utilise le mot clef
explicit dans la dclaration du constructeur.

3.1.7. destruction
en gnral, les objets qui ont t crs doivent faire un peu de mnage quand on les jette
cela se produit automatiquement la sortie du bloc de porte pour les variables automatiques, et explicitement par
appel de delete pour les objet allous
C++ garantit l'appel automatique des destructeurs des objets crs dans ces deux situations, dans l'ordre inverse des
appels de constructeurs correspondants
le destructeur pour une classe X porte le nom ~X

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

33

Cours de programmation

Laurent Henocque

Le langage C++

individu::individu(char *nom):_age(0){
_nom = new char [1+strlen(nom)];//acquisition de mmoire
strcpy(_nom,nom);
}
individu::~individu(){
delete[] _nom;//libration dans le destructeur
}

il ne prend aucun paramtre


il est unique, et peut tre virtuel (ildoit souvent ltre)
le rle d'un destructeur est de librer les ressources acquises lors de la construction ou de la vie de l'objet
pour dtecter un ventuel accs un objet dj dtruit, un constructeur peut (mais ne doit pas) mettre les donnes
membres de l'objet dans un tat incorrect mais reconnaissable

3.1.8. fonctions inline


les classes implantent souvent de nombreuses petites fonctions, qu'il est utile de programmer "en ligne"
notamment, l'accs au donnes est souvent interfac par de petites fonctions d'accs, qui permettent notamment le
mode "lecture seule"
l'appel d'un fonction est normalement plus coteux que l'accs une donne membre : ce n'est pas le cas des fonctions
d'accs "inline"
// file individu.h
class individu {
char * _nom;//private
int _age;//private
public:
const char *getnom(){return _nom;}

//inline car dans la classe

int getage();
};
inline int individu::getage(){return _age;}

//inline explicite hors classe

cela sert un objectif de performance : pas de surcot la dfinition d'une classe "propre"
les fonctions membres dfinies au sein de la dfinition de la classe sont automatiquement inline
elles peuvent tre dclares hors de la classe

3.2. interfaces et mise en oeuvre


brivement ici, on peut dire qu'une bonne classe est une boite noire, prestataire de services, dont les modalits
d'utilisation ne sont pas sensibles aux modalits d'implantation. Le cours de conception oriente objet complte et raffine ces
notions.
ex : objets containers

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

34

Cours de programmation

Laurent Henocque

Le langage C++

3.2.1. modification de mise en oeuvre


modifier l'implantation d'une classe est sans effet pour ses utilisateurs tant que la partie publique reste inchange, ainsi
que les dclarations des fonctions membres
les utilisateurs doivent recompiler aprs modification de la partie prive, notamment cause du changement possible
de la taille de la structure, ou la transformation de certaines fonctions en virtuelles
on doit permettre que des fonctions inline puissent atteindre les champs privs d'un objet
un bon compilateur peut dterminer quels fichiers doivent absolument tre recompils lors du changement d'une
interface
on doit gnralement garantir la compatibilit ascendante d'une bibliothque : les programmes crits avec une
ancienne version doivent compiler et tourner sans changement avec la nouvelle
si la nouvelle implantation de fonctions (constructeurs) requiert plus de paramtres, des valeurs par dfaut permettent
de garder l'ancienne version
une fonction qui attend un paramtre de type X peut tre transforme pour accepter un paramtre de type const X&
sans changement pour ses utilisateurs

3.2.2. un exemple de classe


programmer avec des classes demande plus de rflexion priori qu'avant, notamment car chaque classe dcrit un type
de donnes dont il faut prvoir toutes les utilisations ralistes, et dvelopper en permettant au minimum la rusabilit et la
scurit
class intset {
int _cursize;
int _maxsize;
int *_array;
public:
intset (int m, int n);//au moins m entiers dans 1..n
~intset();
int hasmember(int t)const;// t est il un membre
void add(int t);//ajouter t
//itration
void start(int&v)const{v=0;}
int isnotfinished(int&v)const{return v<_cursize;}
void next(int&v)const{v++;}
int getvalue(int&v)const{return _array[v];}
};
int intset::intset(int m, int n){/*alloue et initialise*/}
int intset::hasmember(int t)const{/*dichotomique ds tab tri*/
void intset::add (int t){/*insertion ds l'ordre avec dcalage*/}

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

35

Cours de programmation

Laurent Henocque

Le langage C++

3.2.3. un exemple de classe


les fonctionnalits d'itration permettent un programme d'utiliser la classe malgr un changement de reprsentation
interne : on n'accde jamais aux donnes membres
main(){
intset is(15,15);
is.add(1);
is.add(7);
int num;
for (is.start(num) ; is.isnotfinished(num) ; is.next()){
cout << "num : "<< num <<" val : "<< is.getvalue(num);
cout << endl;
}

3.3. autres caractristiques des classes


3.3.1. fonctions amies
dans une classe, la dclaration "friend" permet d'accorder l'accs au membres privs ou protected des fonctions
isoles, ou toutes les fonctions d'une classe
cela sert notamment quand une opration requiert des donne membres de deux ou plusieurs classes : par exemple
pour multiplier un vecteur par une matrice
class vector {
friend vector mult(const vector&,const matrix&);
};
class matrix {
friend vector mult(const vector&,const matrix&);
};

une fonction amie peut tre elle mme une mthode


class Y{
friend void X::f();
};

ou bien tre n'importe quelle fonction d'une classe donne


class X{
friend class Y;
};

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

36

Cours de programmation

Laurent Henocque

Le langage C++

une fonction amie fait partie de l'interface de programmation d'une classe

3.3.2. qualification des noms de membre


dans certaines situations on doit dsigner explicitement le nom de la classe d'un membre
on utilise l'oprateur de rsolution de porte"::"
class X{
int m;
public:
int getm()const{return m;}
void setm (int m) {X::m=m;}

//le paramtre masque le nom du membre

};

on peut aussi accder des symboles globaux masqus par un symbole local
class MyFile {
void open(){

::open();//appelle la fonction globale "open"

}
};

3.3.3. classes embotes


une classe peut tre dclare dans une autre, ce qui rduit les symboles globaux en circulation
class X{
struct Y{
int i;
Y():_i(0){}
};
Y* head;
public:
X():head(0){}
};

cela sert en cas de besoin reconnu, pour des classes embotes simples. Le mcanisme utile (accs par la seule classe
englobante) peut tre obtenu parl'utilisation de classes amies :
class Y{ friend class X;
int i;
Y():_i(0){}

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

37

Cours de programmation

Laurent Henocque

Le langage C++

};
class X{
Y* head;
public:
X():head(new Y()){}
};

Un type embot peut tre atteint directement par qualification de porte : X::Y. Une classe imbrique possde le mme
statut que tout autre classe. Un membre qui retourne une valeur d'un type embot se dclare ainsi :
T1::T2 T1::f();

3.3.4. membres statiques


il est parfois ncessaire de stocker des informations relatives un classe (compter les instances, maintenir une liste des
instances cres )
exemple : la bibliothque iostream compte le nombre d'instances de cout cres puis dtruites afin de savoir quand
fermer le fichier de sortie standard
cela se fait en dclarant des membres statiques dans la classe
class obj {
static obj * listeDesObj; // liste des obj est une variable globale
};

un membre statique est une donne globale, dont la visibilit et restreinte la classe, sauf s'il est public
une fonction membre statique n'est visible que dans le fichier qui la dfinit
Attention : il y a ncessit de dfinir et initialiser les membres statiques dans le fichier .cc, car leur dclaration "static"
dans la classe n'est qu'une dclaration

3.3.5. pointeurs sur fonctions membres


on peut prendre l'adresse de membres,que ce soient desfonctions ou des donnes
ce mcanisme ncessaire en C est moins souvent utile en C++ grce aux fonctions virtuelles
struct cl {
char *v;
void print (int x){cout << v <<< x << endl;}
cl(char *_v):v(_v){}
};
typedef void (cl::*PMFI)(int);
int main() {
cl z1("z1");
cl z2("z2");

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

38

Cours de programmation

Laurent Henocque

Le langage C++

cl * p = &z2;
PMFI pf = &cl::print;
z1.print(1)
(z1.*pf)(2);//parenthses obligatoires
z2->print (3);
(z2->*pf)(4);//parenthses obligatoires
}

3.3.6. structures et unions


une structure est une classe publique par dfaut(et une classe est une struct, prive par dfaut)
struct S {}; quivaut class S {public:};

une union nomme est une struct dont chaque membre possde la mme adresse, utile dans le cas rarissime o l'on sait
qu'un seul membre d'une structure est utis un moment donn. Une programme C++ avec des unions est en gnral un
mauvais programme C++
union token {
char *p;
char v[8];
long i;
double d;
token(const char*);//pb : p ou v ? : dcid par programme par ex
token (long _i):i(_i){}
token (double _d):d(_d){}
};

le compilateur ne sait pas quelle est la donne valide un moment donn : pas de vrification
on peut utiliser des constructeurs surchargs pour initialiser une union
on a un problme de vrification de conformit au type : on encapsule souvent l'union dans une classe qui mmorise le
type, ne serait-ce que pendant les phases de dveloppement et de tests, afin d'viter de douloureuses msaventures.
struct Token {
enum Type {Double, Long, StringPtr, StringArray};
private:
Type type;
union token {
char *p;
char v[8];
long i;
double d;
};

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

39

Cours de programmation

Laurent Henocque

Le langage C++

public:
Token(char *pp);
Token(long ii):i(ii),type(Long){}
Token(double dd):d(dd),type(Double){}
long& ival(){assert(type==Long);return i;}
long& dval(){assert(type==Double);return d;}
long& sval(){assert(type==StringPtr);return s;}
long& aval(){assert(type==StringArray);return v;}
};

de tel classes unions type maintenu fournissent une solution lgante pour dcrire des fonctions arguments
variables scurises
extern Token NOTHING;
void message(const char * pattern, Token a1=NOTHING, Token a2=NOTHING, Token
a3=NOTHING);

les constructeurs permettent alors la conversion lors de l'appel en passant des arguments normaux, qui sont
rceptionns en toute scurit aprs l'appel

3.4. construction et destruction


quand une classe possde un constructeur (toujours!) il est appel chaque cration d'objet :
automatique (variable locale, conversion dans une expression, paramtre formel)
statique (variable globale)
dynamique (allou par new)
membre (d'une classe ou d'un tableau)
retourn par une fonction
et galement lors de tout appel explicite du constructeur (locale muette)

3.4.1. variables locales


le constructeur est appel chaque passage du flux de contrlesur la dclaration
les destructeurs sont appels en ordre inverse des constructeurs
on a gnralement un problme avec la copie
table t1(100);
table t2 = t1;

//constructeur par copie

table t3(200);
t3 = t2;

//oprateur d'affectation

le destructeur est appel pour t2, mais pas le constructeur, et le t3 original pas dsallou
pour viter ces problmes il faut dfinir le constructeur par copie et l'affectation

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

40

Cours de programmation

Laurent Henocque

Le langage C++

3.4.2. mmoire statique


les objets statiques et globauxsont allous dans l'ordre de leur dfinitionset dsallous dans l'ordre inverse
ils permettent de faire excuter du code avant et aprsmain
un appel de exit() appelle les destructeurs de globales avant de sortir (et peut donc boucler), ce qui n'est pas le cas de
abort()
parfois, il est pratique dans une librairie de dfinir un type ayant pour seul but d'initialiser la librairie par son
constructeur
la bibliothque iostream utilise ces mcanismes pour ouvrir et fermer les fichiers standard (cout, cin et cerr sont des
variables statiques)

3.4.3. mmoire dynamique


un objet dynamique est allou avec l'oprateur new doit tre libr par delete. Si c'est un tableau, il doit tre libr par
delete[].
class A{};
A* ptr = new A;

// pointeur vers un seul A

A* tab = new A[25];

// pointeur vers 25 A conscutifs

delete ptr;

// ok

delete tab;

// erreur

delete[] tab;

// ok

on ne peut allouer un tableau d'instances d'une classe que si elle possde un constructeur sans argument
oublier d' appeler delete perd dfinitivement de la mmoire
appeler delete deux fois sur un mme pointeur est probablement fatal

3.4.4. objets membres d'autres objets


les membres d'une classe doivent tre initialiss partous les constructeurs
les arguments du constructeur de membre sont placs dans la dfinition, entre la parti
e dclaration et les accolades
les membres sont initialiss dans l'ordre de leurdclaration dans la classe, et seront dtruits dans l'ordre inverse
class A {
int i;
A(int ii):i(ii){}
};
class B {
int j;
A a;
B(int i, int jj);
};
B::B(int i, int jj) : a(i),j(jj){/*des acquisitions de ressources/}

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

41

Cours de programmation

Laurent Henocque

Le langage C++

3.4.5. tableaux d'objets


la dclaration d'un tableau d'objets d'une classe ayant un constructeur ncessite que la classe ait un constructeur ne
prenant pas de paramtre
C++ ne permet pas de passer de paramtres pour la construction de tous les objets d'un tableau
on contourne cette difficult en stockant des valeurs par dfaut d'initialisation dans des variables statiques de la classe
le destructeur doit tre appel sur chaque objet du tableau : il faut utiliser delete[] et non delete
delete[] utilise une information de taille stocke ct de la zone alloue

3.4.6. allocation de petits objets


l'allocateur standard peut tre inefficace si l'on doit allouer et dsallouer de trs nombreux petits objets
on peut avoir de meilleures stratgies d'allocation quand on traite des objets d'un seul type
il est possible de dfinir des oprateurs new et delete pour une classe, appels en priorit s'ils existent
class X{
void *operator new (size_t);
void operator delete(void*, size_t);
};

l'oprateur new global reste accessible si besoin est par ::new, sauf s'il a t lui mme masqu, ce qui est trs
dsagrable
si l'on implante un allocateur pour un besoin spcifique, il est bon de pas masquer pour la classe l'oprateur new
habituel : on surcharge donc new en mentionnant un argument supplmentaire
typedef HEAP ;
class X{
void *operator new (size_t,HEAP);//spcifique
void *operator new (size_t){return ::new char[size_t];};//standard
void operator delete(void*, size_t);
};

noter que si l'on a plusieurs "new" on a un seul delete, ce qui peut poser quelques problmes
enfin, si l'on alloue des blocs de taille constante, on peut ignorer l'argument taille de new, et utiliser une version de
delete sans argument taille
on peut jusqu' doubler la vitesse d'un programme en allouant bien les petit blocs beaucoup utiliss (maillons de liste
chaine par exemple)

3.5. exercices
3.5.1. exercice
concevoir une structure Noeud d'arbre binaire avec des constructeurs et destructeurs

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

42

Cours de programmation

Laurent Henocque

Le langage C++

3.5.2. exercice
modifier la classe intset pour en faire un ensemble de Noeuds, un ensemble de chanes

3.5.3. exercice
dfinir une classe pour l'analyse, la mmorisation et l'valuation d'expressions arithmtiques entires simples (le
constructeur reoit la chaine de caractres qui reprsente l'expression)

3.5.4. exercice
concevoir une classe table des symboles, et une classe "entre de table des symboles"

3.5.5. exercice
utiliser la construction d'un objet global pour afficher du texte avant le main et aprs

3.6.

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

43

Cours de programmation

4.

Laurent Henocque

Le langage C++

Hritage
4.1. introduction et tour d'horizon

l'hritage permet de dcrire une hirarchie entre les diffrents types de donnes manipuls par un programme. La
relation fondatrice de cette hirarchie est la relation "est une sorte de". Cette relation logique est celle qui doit guider la
dcision d'untiliser l'hritage autant que possible.
les classes hrites permettent de dcrire despoints communs entrediffrentes classes
l'hritage multiple permet de combiner des caractristiques issues de plusieurs super classes soit par ajout simple (on
parle alors de mixin - pour mixing in -) soit pour hriter d'une part d'une implmentation, d'autre part d'une interface de
programmation. Cess notions sont dtailles dans le cours de programmation objet.

4.2. hritage : classes drives


Exemple si nous disposons d'une classe employ et d'une classe manager, il est probable que manager hrite de
employ. Un manager est une sorte de employ, avec certaines prrogatives.
class employ {};
class manager : public employ {};

C++ permet la conversion implicite de la classe drive vers la classe de base


,
et la conversion explicite (cast) de la classe de base vers la classe drive sans qu'aucune vrification ne soit possible
par le compilateur dans ce deuxime cas

4.2.1. fonctions membres


un membre priv de la classe de base ne peut pas tre accd par un membre de la classe drive.
si on dsire permettre cette possibilit, on utilisele spcificateur d'accs protected au lieu de private

4.2.2. constructeurs et destructeurs


la classe de base est vue exactement comme un membre de la classe drive. Le constructeur de chaque classe hrite
doit tre appel comme les constructeurs des membres
class B : public A {
int i;
B():A(),i(0){} // la construction de B requiert celle de A et de i
};

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

44

Cours de programmation

Laurent Henocque

Le langage C++

4.2.3. hirarchies de classes


Pour savoir quel type rel appartient un objet dont on connat la classe de base, il y a trois solutions :
garantir que l'on ne peut pointer que sur des objets d'un seul type
utiliser un champ de type
utiliser des fonction virtuelles

4.2.4. champs de type


une information de type peut tre fournie par une classe au moyen d'une variable statique (globale) dclare dans la
classe, et dont la valeur (ou simplement l'adresse) permet de savoir quel classe appartient un objet.
on peut mme construire automatiquement l'arbre des classes d'un programme ce qui permet de savoir si un objet
appartient une "sous classe" d'une classe donne. (Exercice !)

4.2.5. fonctions virtuelles


une fonction virtuelle est lie dynamiquement par le compilateur : la fonction appele sur un objet dont le type n'est
pas connu avec prcision (car son pointeur est manipul au travers d'un pointeur vers une super classe) est celle qui
correspond sa classe de cration. En C++ le liage par dfaut est statique : la fonction appele est celle dfinie pour la classe
identifie par le type du pointeur utilis (elle peut bien sr tre hrite), mme s'il y a eu un cast. Dans le doute, une fonction
memebre est virtuelle.
une fonction virtuelle est marque par le qualificateurvirtual.
elle peut tre dfinie dans la classe ou elle est dclare : la classe de base est capable de fournir une implantation par
dfaut, masque par les futures sous classes ventuellement
ou alors elle est virtuelle pure, dfinie par 0
class Z {
virtual f()=0; // virtuelle pure
};

l'ensemble des fonctions virtuelles d'une classe dfinissent son


protocole.

4.3. classes abstraites


il s'agit de classes qui groupent des fonctionnalits et des donnes utilises par plusieurs classes drives, mais dont
aucun objet rel ne peut tre issu. Toute classe qui comporte au moins une virtuelle pure est abstraite d'office : le compilateur
refuse d'en crer des instances, mme si l'on n'utilise pas les fonctions non dfinies.
exemple : la classe forme dans un systme graphique est abstraite. On en drive les classes concrtes carr, rond,
triangle, qui pour lesquelles les fonctions dessine, efface, pivote du protocole de forme peuvent recevoir une implantation.
les classes abstraites permettent de fournir une interface sans exposer les dtails de ralisation.
une classe abstraite ne peut tre utilise comme type d'argument, de valeur de retour, ou comme type d'une conversion
explicite. Par contre, dans tous ces cas, on peut utiliser un pointeur vers la classe abstraite.

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

45

Cours de programmation

Laurent Henocque

Le langage C++

4.4. hritage multiple


C++ permet l'hritage multiple. Le problme de conflit potentiel lors de l'hritage de plusieurs classes qui possdent
des membres de mme signature doit tre rgl par le programmeur en spcifiant explicitement quelle est la classe de la
fonction appele. On crit A::f() ou B::f() si on parle du f de A ou du f de B
on peut dfinir une nouvelle fonctionf() dans la classe drive qui appelle celle qui convient ou les deux si ncessaire.
il reste un problme li la mention explicite du type de la classe de base : si on insre une classe dans la hirarchie de
classes, on doit modifier les programmes des classes drives (le nom de la super classe change). Une solution est de dfinir
un type local appelinherited, ou superclass pour identifier la classe d'hritage principal par exemple :
class manager : public worker, public A, public Z {
typedef manager inherited;
void f(){ inherited::f()}
}

4.5. hritage virtuel


Il arrive que des classes soeurs aient besoin de partager de l'information. Cela peut tre obtenu par l'hritage commun
d'une classe de base hrite virtuellement.Un tel exemple de partage est le nom de l'objet dans un systme objets nomms.
dans toute instance d'une classe donne, on trouveune seule instance dechaque classe hrite virtuellement
class B : public virtual A {}
class C : public virtual A {}
class D : public C, public B {} // un seul A dans D

quand une classe hrite virtuellement, une fonction virtuelle de la classe de base peut tre effectivement implante par
une classe soeur.
l'hritage virtuel est implant au travers de pointeurs internes l'objet et prsente un cout non nul (compens par la
non rptition)

4.6. contrle d'accs


private : la classe seulement et les amies
protected : la classe et toutes ses filles venir et les amies
public : tout le monde

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

46

Cours de programmation

Laurent Henocque

Le langage C++

4.7. mmoire dynamique


4.7.1. surcharge de new et delete
une classe de base peut redfinir new et delete
void * operator new (size_t);
void operator delete (void *, size_t);

tant que le type rel est le type de l'objet spcifi, le compilateur connat la taille de l'objet dtruire.
dans le cas contraire, on doit utiliser un destructeur virtuel. Attention, il est toujours prfrable d'utiliser des
destructeurs virtuels !!!

4.7.2. constructeurs virtuels


on a parfois besoin de fonctions de clonage de structures, pouvant tre apparentes des constructeurs virtuels (par
dfinition un constructeur ne peut pas tre virtuel car on dsigne le nom de la classe instancier). On dfinit un systme de
clonage ainsi :
virtual base * new_base () { return new base()};

et dans une classe drive :


base * new_base () {return new drive ()};

4.7.3. oprateur de placement


On peut vouloir allouer des objets dans un espace prdtermin (placement explicite):
void * operator new(size_t, void *p);
char buffer[sizeof(X)];
X*p = new(buffer) X(i);

On peut vouloir placer dans un espace de stockage particulier, ce qui est utile pour faire du backtracking, ou stocker
des donnes en diffrents espaces (rmanents, partags, etc.) :
class storage {
virtual void * alloc(size_t);
virtual void

free(void *, size_t);

};
void *operator new(size_t s, storage & loc) {
return loc.alloc ( s );
}

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

47

Cours de programmation

Laurent Henocque

Le langage C++

storage maPileDobjets (1024);


X* p = new(maPileDobjets) (args);

Lors de la destruction, il faut appeler explicitement le destructeur (qui doit donc tre virtuel si l'opration est faite par
free soi mme), et ensuite prvenir le systme de stockage:
p->~X(); maPileDobjets.free(p);

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

48

Cours de programmation

5.

Laurent Henocque

Le langage C++

Dclarations et constantes
5.1. dclarations
en C++ un nom doit tre dclar
la dclaration stipule le type de l'objet
syntaxe : nom de type + nom de variable [ + "=" + valeur];
quand il y a valeur, c'est une dclarationet une dfinition.
Exemples : dterminer les dfinitions :

char ch;
int count =1;
char * name;
struct complex {float re, im};
complex cvar;
extern complex sqrt(complex);
extern int error_number;
typedef complex point;
float real(complex *p) { return p->re;};
const double pi = 3.1415926...;
struct user;
template<class T> abs(T a) {return a<0 ? -a : a; }
enum car {peugeot, citroen, volvo};

certains types ont une valeur initiale permanente : structures, fonctions


toute dclaration prcisant une valeur est une dfinition

5.1.1. porte des symboles


une dclaration introduit un nom (symbole) dans une porte
deux dclarations dans le mme bloc sont impossibles
une dclaration dans un bloc englob peut masquer une dclaration plus globale
cette facilit doit tre utilise avec prcautions : un bon moyen est de donner des noms longs aux variables (viter i, z,
t, x etc)
espaces de porte en C++:
bloc

{ }

classe

class { } ;

fonction

T f() { }

fichier

static

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

49

Cours de programmation

programme

Laurent Henocque

Le langage C++

extern

5.1.2. porte des symboles


oprateur de rsolution de porte

::

int x;
main(){
int x= 1;
x = x + ::x;
}

il n'y a aucun moyen d'utiliser un nom local masqu


la porte commence ds que le nom est connu:
int x = x;

la porte commence ds la dclaration


int x;
main(){
int y = x+1; // x global
int x = 2;
y = x+1; // x local
}

5.1.3. objets et l_values


un objet est une rgion de la mmoire
une l_value est un objet ou une fonction
en C++ les l_values const ne peuvent tre affectes (places gauche du signe =)
une l_value qui n'est pas dclare const est dite modifiable

5.1.4. dure de vie


un objet est normalement dtruit quand le flux de contrle sort de la zone de porte
sauf s'il est static
dans ce cas, il est initialis la premire fois que le flux de contrle passe par lui
int a = 1;
void f() {
int b = 1;
static int c = a;

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

50

Cours de programmation

Laurent Henocque

Le langage C++

a++, b++, c++;


cout << a << " " << b << " " << c << " " << endl;
}
main(){
while (a<4) {f();}
}

5.2. les noms


le premier caractre doit tre une lettre
soulign "_" est une lettre
l'analyse considre le nom le plus long possible
certains caractres ($) rendent les programmes non portables
les mots clef ne sont pas des noms
majuscules et minuscules sont distinctes

5.3. les types


tout nom est associ un type
le nom du type dtermine les oprations possibles sur le symbole associ
un nom de type est normalement utilis pour spcifier le type d'un autre nom
deux oprations particulires s'appliquent aux seuls noms de type :
sizeof - pour connatre la taille d'une structure en multiples de la taille d'un char
new - pour allouer dynamiquement une instance du type
int main() {
int * p = new int;
cout << sizeof(int) << endl; // taille d'un int
cout << sizeof(p) << endl;

// taille d'un pointeur

cout <<sizeof(*p) << endl;

// taille d'un int

un nom de type peut aussi tre utilis pour spcifier une conversion explicite d'un type vers un autre
char *p;
long l = long(p); // convertit de pointeur vers long

5.3.1. types fondamentaux


char, short int, int, long int (signed, unsigned)

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

51

Cours de programmation

Laurent Henocque

Le langage C++

float, double, long double

tout type manquant est considr comme int


tout type est sign par dfaut (sauf char)
char, unsigned char et signed char sont trois types distincts, mais ont la mme taille
char est suffisamment grand pour contenir un caractre du jeu standard, et est sign ou non sign suivant les
caractristiques de la machine (critre de performance)
signed char est donc un entier de la mme taille
unsigned char est plus portable, mais peut tre considrablement moins efficace
int est optionnel dans les combinaisons ("unsigned" au lieu de "unsigned int")

5.3.2. tailles des types


les tailles des types respectent les rgles suivantes :
sizeof(char) == 1
char < short < int < long
float < long < double
T == signed T == unsigned T
char >= 8, short >= 16, long >= 32

on est garanti qu'un char contiendra les entiers de 0 127 ; supposer plus est non portable
donner le type unsigned n'empche pas de copier une valeur ngative : cela n'agit que sur les rgles de l'arithmtique
unsigned int bitmask = -1;

// warning peut tre

unsigned int bitmask2 = ~0;

// meilleur

5.3.3. la conversion implicite


lors de la conversion d'un type en un autre, si possible, l'information de dpart est conserve
ds que l'on perd des bits, on a des problmes :
int i =

256+255;

char c = i; //c = 255


int j = c;
// j vaut 255 sur une machine ou le char est non sign (68XXX)
// j vaut -1 sur une machine o le char est sign (VAX)

5.3.4. les types drivs


partir de type(s) de base, on peut dfinir des types drivs :
*

pointeur,

&

rfrence,

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

52

Cours de programmation

Laurent Henocque

[]

tableau,

()

fonction,

struct

structure

Le langage C++

exemples :
int* a;
float v[10];
chat-r *p[20];
void f(int);
struct str{int a; char *s;};

les parenthses peuvent tre utilises dans les cas o la prcdence des oprateurs n'est pas celle souhaite
int *v[10];
int (*p) [10];

// tableau de 10 pointeurs vers des int


// pointeur vers un tableau de 10 int

on peut dclarer plusieurs noms ensemble spars par une virgule (mais il faut viter de le faire)
int *p, q; // q n'est pas un pointeur

5.3.5. les types void et void*


type fondamental, ne peut servir que comme lment d'un type driv
un pointeur de n'importe quel type peut tre affect une variable de type void *
utile pour les fonctions qui ne sont pas autorises faire d'hypothse sur le type de la donne qu'elles manipulent
exemple de malloc :
void * malloc(size_t);
void free (void *);
main(){
int * s = (int *) malloc(10*sizeof(int));

free (s);
}

5.3.6. pointeurs
pour la plupart des types T, ladclaration d'un pointeur sur T est noteT*
int * pi;
char **ps;
int (*vp)[10];

//pointeur vers pointeur


//pointeur sur un tableau de 10 int

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

53

Cours de programmation

Laurent Henocque

Le langage C++

int (*f) (char, float); // pointeur sur fonction

la drfrence (ou encore indirection) est note


l'arithmtique des pointeurs est possible
exemple : strlen version 1

*V

int strlen(char *p){


int i = 0;
while (*p++) i++;
return i;
}

ou encore

int strlen(char *p){


char * q = p;
while (*q++);
return q-p-1;
}

5.3.7. tableaux
dclaration
T[taille];
les lments sont indexs de 0 taille - 1;
pas de virgule pour les dimensions suprieures a un
int tab[2][5]; //un tableau de deux tableaux de cinq entiers

initialisation statique
char t[]="abcdefghijkl";
char v[2][5] = {{1,2,3,4,5},{'a','b','c','d','e'}};

utilisation
main (){
for (int i=0; i<2 ; i++)
for (int j = 0; j<5 ; j++)
cout << v[i][j] << endl;
}

l'affectation un tableau est impossible, il faut faire une copie lment par lment

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

54

Cours de programmation

Laurent Henocque

Le langage C++

5.3.8. pointeurs et tableaux


un tableau est toujours assimilable son premier lment, et rciproquement
les arguments de fonctions de type tableau sont toujours passs sous la forme d'un pointeur vers leur premier lment
il n'y a aucun moyen de provoquer une copie de tableau lors d'un appel de fonction
dans une structure, un tableau diffre d'un pointeur car il est intgr dans la structure (le tableau intgr dans la
structure sera donc copi lors d'une copie bit bit de la structure)
arithmtique :
on peut soustraire deux pointeurs: le rsultat est entier
on peut ajouter ou soustraire un entier un pointeur : le rsultat est un pointeur
le seul type entier qui est garanti de contenir un pointeur converti est "long"
le compilateur ne fait aucun contrle sur la validit d'un pointeur obtenu par calcul

5.3.9. structures
on appelle structure un agrgat arbitraire d'objets de types quelconques
struct adresse {
char * nom;
int numro;
char * rue;
int code;
char * pays;
}; // noter le point virgule final

l'accs aux membres des structures se fait par les oprateurs "" et->"
"
adresse add;
add.nom = "James Bond";
add.number = 007;
adresse *padd = &add;
cout << padd->nom << " " << padd->number << endl;

l'initialisation statique est possible comme pour les tableaux


adresse A={"Pernod",45,"rue des vagues", 13004, "Marseille"};

les comparaisons == et != sont non dfinies (mais dfinissables par le programmeur)


l'affectation d'une structure une autre fait une copie bit bit

5.3.10.

structures

le nom d'un type est utilisable ds qu'il a t vu, pourvu que son emploi ne ncessite pas de connatre la taille de la
structure

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

55

Cours de programmation

Laurent Henocque

Le langage C++

struct maillon {
maillon *suivant;

};

et encore :
class S;
extern S a;
S f();
void g(S);

par contre :
void h(){
S a; //erreur S inconnu
f(); //id
g(a);//id
}

on ne peut dclarer une instance d'un type structure avant que la dfinition du type n'ait t vue entirement
struct mauvaise {
mauvaise elem;

// refus

};

5.3.11.

structures

la gestion des rfrences croises (list de links qui pointent sur la liste) se fait par des dclarations pralables :
struct link;

//dclaration pralable

struct list {
link *l;
};
struct link {
link *next;
list *pere
};

5.3.12.

quivalences de types

deux types sont diffrents mmes s'ils ont les mmes membres et ne sont pas interchangeables

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

56

Cours de programmation

Laurent Henocque

Le langage C++

struct s1 {int a;};


struct s2 {int a;};
s1 x;
s2 y = x;

//impossible

int i =x;

//impossible

on utilise typedef pour dfinir de nouveaux (noms de) types partir d'existants
typedef char * String;
String p1, p2;
char * q1,q2; //attention ici q2 est char !

5.3.13.

rfrences

une rfrence est un nom alternatif un objet : les oprateurs agissent sur l'objet rfrenc
au travers de la rfrence
int i = 0;
int& j = i;
j++;

//i vaut maintenant 1

l'initialiseur d'une rfrence doit donc tre une l_value (sauf cas particulier ci aprs)
les rfrences servent essentiellement pour
le passage d'arguments par adresse
les valeurs de retour de fonction
les dfinitions d'oprateurs
une rfrence est un alias : on ne peut prendre une double rfrence
int i = 0;
int& j = i;
int&& k = j; // impossible

5.3.14.

rfrences

une rfrence est un pointeur masqu (constant)


le type de la rfrence et de l'objet rfrenc doivent tre strictement compatibles
on peut prendre une rfrence une constante ; dans ce cas, linitialiseur peut ne pas tre une l_value, ni mme tre du
type demand : des convertisseurs sont appels automatiquement pour initialiser une variable temporaire
double& cd1=1;
const double& cdr = 1;

//impossible
//ok

les rfrences permettent l'utilisation d'un appel de fonction en l_value ( gauche du signe =)

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

57

Cours de programmation

Laurent Henocque

Le langage C++

struct assoc {
char *nom;
int value;
};
int&value(char*nom){
assoc *courant;
//recherche de nom
return courant->value;
}
main(){
char buf[80];
while (cin>>buf) {
value(buf)++;
}
}

5.3.15.

rfrences

les rfrences permettent de faire voluer une interface fonctionnelle qui copie des structures (petites) vers une
interface qui copie des pointeurs sans modifier les programmes qui utilisent cette interface
struct data {};
//version 1
data f(){}
main(){
data res=f();
}
//version 2
const data& f(){}
main(){
data res=f(); // cet appel ne change pas
}

5.4. les littraux


5.4.1. constantes entires
dcimales :0, 123, 213546887778984

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

58

Cours de programmation

Laurent Henocque

Le langage C++

octales (commencent par un zro) : 077, 0732,


hexadcimales (commencent par un zro et un x) : 0x1a6f
les suffixes U et L identifient des constantes non signes ou long : 3U, 3L

5.4.2. constantes virgule flottante


type double par dfaut
1.3, .23, 1.2e-15,
le suffixe f identifie les constantes de type float : 1.56e4f

5.4.3. constantes caractres


'a', '\n', '\a', '\0'
ce sont des constantes symboliques pour les valeurs entires du jeu de caractre de la machine
caractres spciaux:

passage la ligne
tabulation horizontale

NL(LF)
HT

tabulation verticale
effacement

\n

\t
VT

BS

\v
\b

retour chariot

CR

\r

saut de page

FF

\f

alerte

BEL

\a

anti slash

\\

caractre

\?

caractre

'

\'

caractre

"

\"

caractre valant 0

NUL

\0

caractre en octal

ooo

\ooo (1,2,3 chiffres)

caractre en hexa

hhh

\xhhh (autant que l'on veut)

utiliser des constantes explicites (octales ou hexa) rend les programmes non portables entre machines ayant des jeux de
caractres diffrents
une constante octale ou hexa se termine au premier caractre non octal ou non hexa
char *s="voici un caractre idiot \x8E345AB insr dans une chane";

5.4.4. littraux chanes


texte entre guillemets, caractre nul terminal
sizeof donne la longueur de la chane + 1
strlen donne la longueur de la chane
le passage a la ligne dans une chane est interdit

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

59

Cours de programmation

Laurent Henocque

Le langage C++

la concatnation est automatique


il est conseill de toujours utiliser trois chiffres pour les caractres en octal
s= "cette chaine provoque
une erreur";
char s[]=

"ca fait " "bien plaisir "

char s[]=

"parlez aprs le bip\007\n";

char s[]=

"la fin disparat\000imbecile";

char s[]=

"a\xOfah\0129"; //'a' '\xfa' 'h' '\12' '9'

char s[]=

"a\xfah\129"; //'a' '\xfa' 'h' '\12' '9'

char s[]=

"a\xfad\127"; //'a' '\xfad' '\127'

"par rapport a C";

5.4.5. zro
c'est un int utilisable comme constante de tout type entier, pointeur, ou flottant.
void f(int);
void f(char *);
main(){
f(0); // le compilateur refuse cet appel : ambigu
f(int(0)); //ok
f((int)0); //ok, coercition a la C
f((char *)0);//ok
}

gnralement, quand c'est possible, il correspond un mot dont les bits sont tous a zro
unsigned bitmask = ~0; // bits tous a un

C++ recommande d'utiliser 0 pour dsigner le pointeur nul plutt que la macro NULL habituellement dfinie dans des
fichiers include de l'utilisateur ou des bibliothques

5.5. les constantes nommes


5.5.1. le mot clef const
le mot clef const ajout a un type permet de transformer l'objet correspondant en constante plutt qu'en variable
une donne const ne peut pas tre modifie dans tout l'espace de sa porte
une constante doit tre initialiseds sa dclaration
const int tailleTab =4;
const int tab[]={1,2,3,4};

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

60

Cours de programmation

Laurent Henocque

Le langage C++

const est un modifieur de type : il dcrit comment une donne doit tre utilise
const char * peek(int i) {
return tab[i]; //l'appelant ne peut pas changer tab[i]
}
void f(const char *);
void f(char *);

//une autre fonction f

5.5.2. le mot clef const


utilisations de const :
const char *s1="123456789";
s1++;

//ok

s1[3]='e';

//interdit

char *const s2="123456789";


s2++;

//interdit

s2[3]='e';

//ok

const char *const s3="123456789";


s3++;

//interdit

s3[3]='e';

//interdit

void f1(const char *);

//appel possible sur s1, s2, s3

void f2(char *);

//appel possible sur s2

void f3(const char *&); //appel possible sur s1 et s2


void f4(char *&);

//appel impossible dans tous les cas

5.5.3. numrations
pour dclarer des constantes, on peut galement utiliserenum
enum {ASM, AUTO, BREAK}; // dclare trois valeurs

forme quivalente :
const int ASM =0;
const int AUTO =1;
const int BREAK =2;

une numration peut tre nomme. Attention, il ne faut pas utiliser la syntaxe "typedef enum" comme en C.
enum motclef {ASM, AUTO, BREAK}; // motclef devient un type

on a la possibilit de valeurs explicites

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

61

Cours de programmation

Laurent Henocque

Le langage C++

enum data {a=123,b=-78,c=0};

la conversion d'enum vers int est automatique mais la conversion inverse doit tre rendue explicite, ce qui rend souvent
les numrs assez dsagrables utiliser
int i = ASM; // ok
motclef var1 = 2; // rejet
int j = 1;
motclef var = motclef(i); ok

les enumrations sont parfois meilleures que les int correspondants : elles permettent au compilateur de gnrer des
messages si l'un des cas est oubli dans un switch.

5.6. les champs de bits et unions


objectif : gain d'espace mmoire. C'est du HACK rarement utilis sauf dans des programmes de communication.
L'accs un bit est d'ailleurs plus cher que l'accs un int.
on peut obtenir de la place en mettant plusieurs objets de petite taille dans un octet, ou en utilisant le mme espace
des fins diffrentes suivant les cas
c'est rarement immdiatement portable

5.6.1. champs de bits


struct registre {
unsigned int enable :1; //un bit
unsigned int error : 2; //deux bits
unsigned int :2;

//deux bits inutiliss

unsigned int mode : 3;

};

gain hypothtique (la taille du programme et le cot des accs augmente)


utiliss surtout pour accder de faon nomme des parties d' octet

5.6.2. unions
on veut utiliser la mme structure pour stocker des donnes diffrentes suivant les moments
exemple : table des symboles,dont une entre contient nom et valeur, de type entier ou chane.
struct entre {
char type;
char *nom;

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

62

Cours de programmation

Laurent Henocque

Le langage C++

union {char* valeurChaine; int valeurInt;};


};

sur certaines machines, int et pointeur n'auront pas la mme taille : ce n'est pas une simple conversion
certains programmeurs les utilisent pour convertir sans que le compilateur ne rechigne (c'est vilain : mauvaise
utilisation du concept)
struct cast {
union baduse{ //on peut la nommer
int i;
int *p;
};
};
main(){
cast c;
c.i = 12;
int *p=c.p; //a vomir
}

5.7. exercices
5.7.1. exercice
crire un programme qui affiche les tailles de tous les types fondamentaux, et de types pointeurs vers, tableaux et
structures qui en drivent.

5.7.2. exercice
crire un programme qui affiche sous forme d'entiers les adresses relatives de champs de structures

5.7.3. exercice
crire une fonction qui permute deux entiers

5.7.4. exercice
afficher la configuration de bits du pointeur 0 sur votre machine

5.7.5. exercice
afficher tous les caractres utiles et leurs valeurs comme entiers

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

63

Cours de programmation

Laurent Henocque

Le langage C++

5.7.6. exercice
dfinir un fichier de dfinitions pour des types de base de taille connue avec exactitude. (pour la portabilit).

5.7.7. exercice
crire des dclarations pour
un pointeur sur un caractre
un tableau de 10 entiers
un pointeur sur un tableau de chanes de caractres
un pointeur sur un pointeur de caractre
une constante entire
un pointeur sur une constante entire
un pointeur constant sur une constante de type entier
crire les initialisations et compiler

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

64

Cours de programmation

6.

Laurent Henocque

Le langage C++

Expressions et instructions
6.1. sommaire des oprateurs
6.1.1. oprateurs

voici un tableau synoptique des oprateurs du langage C++, avec les conventions suivantes :
les oprateurs sont prsents par ordre de prcdence dcroissant
dans la mme boite, la prcdence est la mme
objet est une expression donnant une instance d'une classe et l_value est une expression reprsentant un objet non
constant
::

rsolution de porte

classe::membre

::

global

::nom

slection de membre

objet.membre

->

slection de membre

objet->membre

[]

indexation

pointeur[expression]

()

appel de fonction

expression(liste

()

construction de valeur

type(liste dexpression)

sizeof

taille d'un objet

sizeof expression

sizeof

taille d'un type

sizeof (type)

dexpression)

6.1.2. oprateurs
++

pr et post incrmentation

++lvalue et lvalue++

--

pr et post dcrmentation

--l_value et l_value--

complment 2

~expr

non

!expression

-,+

moins et plus unaires

-expression, +expr

&

adresse de

&lvalue

drfrence

*pointeur

new

crer

new type

delete

dtruire

delete pointeur

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

65

Cours de programmation

Laurent Henocque

Le langage C++

delete[]

dtruire tableau

delete[] pointeur

()

coercition (conversion)

(type) expression

.*

section de membre

objet.ptr_de_membre

->*

section de membre

ptr->ptr_de_membre

*, /, %

fois, divise, modulo

expression op expression

+, -

plus, moins

expression op expression

<<, >>

dcalage

expression op expression

<, <=, >, >=

comparaisons

expression op expression

==, !=

gal diffrent

expression op expression

&

et bit bit

expression op expression

6.1.3. oprateurs
^

ou exclusif bit bit

expression op expression

ou bit bit

expression op expression

&&

et logique

expression op expression

||

ou logique

expression op expression

? :

expression conditionnelle

expression ? expression :

affectation simple

l_value = expression

op=

op et affectation

l_value op= expression

expression

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

66

Cours de programmation

Laurent Henocque

Le langage C++

(op peut valoir +,-,*,/,%,<<,>>,&,|,^)

virgule (squence)

expr,expr

6.1.4. associativit
les oprateurs unaires et d'affectation sont associatifs droite
tous les autres associent gauche, y compris les oprateurs postfixs
a=b=c
*p++

signifie (a=(b=c))
signifie (*(p++))

a<b<c

signifie ((a<b)<c) et non (a<b && b<c)

6.1.5. parenthses
les parenthses permettent de contrler l'associativit, et l'ordre d'valuation en partie du fait de la prcdence des
oprateurs

6.1.6. ordre d'valuation


il n'est pas garanti sauf pour les oprateurs &&, ||, "," qui valuent d'abord la gauche
int i=1;
v[i]=i++;

//v[1]=1 ou v[2]=1;

i=v[i++];

//valeur de i indfinie

les parenthses peuvent imposer un ordre d'valuation


x*y/z

value en (x*y)/z

(x*y)/z

diffre en gnral de x*(y/z) (virgule flottante)

6.1.7. expressions primaires


primaire :
littral
this
:: identificateur
:: operator operateur
:: nom_classe_qualifi::nom (permet A::B::C::size)
( expression )
nom

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

67

Cours de programmation

Laurent Henocque

Le langage C++

nom:
identificateur
operator operateur
nom_fonction_conversion
~nom_classe
nom_classe_qualifi::nom

6.1.8. indexation
a[b] est identique par dfinition *((a)+(b))
ce n'est plus vrai pour l'oprateur [] quand il est dfini pour un type utilisateur videment

6.1.9. appel de fonction


avant l'appel, chaque argument formel est initialis avec l'argument rel
la fonction peut changer la valeur de ses arguments formels non constants
les arguments rels sont intouchs sauf rfrences non const
l'ordre d'valuation des arguments est non dfini
les appels rcursifs sont permis
un appel de fonction dont le type retourn est une rfrence non const est une l_value
une fonction peut tre dclare pour accepter moins d'arguments rels (valeurs par dfaut) ou plus (notation )
dans le cas (pas d'argument formel disponible)
un argument float est converti en double
un char, short, champ de bit ou enum est converti en int ou unsigned int par promotion d'entiers (int de prfrence si la
valeur passe peut tre dcrite par un int)
un objet de classe est pass comme une struct (sinon un appel au constructeur de copie est ralis)

6.1.10.

conversion de type explicite type(liste_expr)

construit une valeur du type partir des arguments


s'il y a plusieurs paramtres, le type doit tre une classe avec un constructeur disponible et non ambigu pour ces
arguments rels
s'il n'y a aucun argument, soit un constructeur existe, soit le rsultat est une donne de valeur indfinie pour le type
void f(int){}
main(){
f(int());//passe un int arbitraire
}

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

68

Cours de programmation

6.1.11.

Laurent Henocque

Le langage C++

accs un membre et ->

l'expression est une l_value ssi le membre en est une


a->nom quivaut strictement (*a).nom

6.1.12.

oprateurs d'incrmentation et de dcrmentation

leur effet sur les pointeurs est celui de l'arithmtique des pointeurs (qu'ils soient prfixes ou postfixes)
char * strcpy (char *p, const char *q) {
while(*p++ = *q++);
return p;
}

6.1.13.

sizeof

sizeof(char) == 1

une taille d'instance est toujours strictement plus grande que 0


sizeof ne peut pas tre appliqu une fonction, un champ de bits, une classe indfinie, void, ou un tableau ayant
une dimension non spcifie
si l'oprande est une expression, elle n'est pas value
si c'est un type, il doit tre entre parenthses
appliqu une rfrence, c'est la taille de l'objet rfrenc
appliqu un tableau, c'est la taille du tableau (nombre total d'octets)

6.1.14.

l'oprateur new

new essaye de crer un objet du type demand, et retourne un pointeur sur cet objet
si l'objet est un tableau, un pointeur sur le premier lment est retourn
new int;

//type int *

new int[20];

//type int *

new int[i][50];

//type int(*)[10]

dans ce cas la premire dimension peut tre variable, les autres constantes
on peut dfinir des allocateurs supplmentaires, ou surcharger l'oprateur new par dfaut
new (a,b,c) T;

//appel de operator new(sizeof(T),a,b,c)

void *operator new(size_t) peut tre appel avec l'argument 0 : un pointeur vers un objet toujours diffrent est retourn
chaque classe peut dfinir un oprateur new pour ses instances

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

69

Cours de programmation

Laurent Henocque

Le langage C++

class T;
new T; //appel de void *T::operator new(size_t) s'il existe
new U;

//appel de void *operator new(size_t) si U non classe

::new V; //appel de void *operator new(size_t) global

6.1.15.

l'oprateur new

un appel new combine allocation et appel de constructeur


new T(a,b,c);

//appel de T::T(a,b,c) sur l'espace allou

la mme syntaxe est utilisable pour les types de base:


int * ipt=new int(200);

on ne peut pas passer de paramtre au constructeur pour les tableaux


un tableau ne peut tre allou que si la classe dcrit un constructeur par dfaut (sans paramtre) qui sera alors appel
pour chaque instance du tableau
new T[10](a,b);//interdit

on peut utiliser explicitement la version parenthse de new si on a un problme cause de parenthses dans le type :
new(int (*[15])(char)); //tableau de 15 pointeurs vers fonctions

6.1.16.

l'oprateur delete

delete doit recevoir un pointeur sur un objet construit par new


delete(0) ne fait rien
pour une classe T, void T::operator delete (void *) est appel prioritairement s'il a t dfini
la valeur de l'objet point est indfinie aprs la destruction
delete appelle le destructeur de l'objet (qui doit tre virtuel)
delete[] d'un pointeur simple est terrifiant
delete simple d'un pointeur vers un tableau est abominable

6.1.17.

conversion de type explicite (coercition


)

toute conversion standard est possible de faon explicite


la notation coercition (cast C) est ncessaire pour convertir vers un type dont le nom est complexe
un pointeur peut tre converti en entier assez grand, et rciproquement
un pointeur sur un type peut tre converti en un pointeur sur un autre type : problmes si incompatibilits d'alignement
si l'autre type est de taille suprieure ou gale, l'aller retour de conversions ne produit pas de changement

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

70

Cours de programmation

Laurent Henocque

Le langage C++

un pointeur vers B peut tre converti en pointeur vers D qui hrite de B si B n'est pas classe de base virtuelle (suppose
que B apparat comme sous objet dans D : les valeurs de ces deux pointeurs sont normalement diffrentes)
le pointeur nul est toujours converti en lui mme
un objet peut tre converti en rfrence X& si un pointeur sur cet objet peut tre converti en X*
la conversion vers une rfrence ne provoque pas d'appel de fonction de coercition, ni de constructeur
le rsultat d'une coercition vers une rfrence est une l_value (le seul cas)

6.1.18.

conversion de type explicite (coercition)

on peut convertir des pointeurs vers fonctions en pointeurs vers objets et rciproquement condition que les tailles
soient compatibles (danger l'utilisation)
un pointeur vers fonction peut tre converti en pointeur vers une autre fonction : l'appel travers ce nouveau pointeur a
des effets imprvisibles
un objet ou une valeur peut tre converti en objet d'une classe seulement si un constructeur ou un oprateur a t
dclar
un pointeur sur membre peut tre converti en un pointeur sur membre diffrent quand :
les deux pointent sur des membres de la mme classe ou
les deux pointent sur des membres de classes dont l'une drive de l'autre sans ambigut
on peut convertir un type constant (objet, rfrence d'une part ou pointeur) vers un type non constant (rfrence ou
pointeur seulement) : on obtient le mme pointeur ou objet. Toutefois son utilisation pour modifier l'objet peut provoquer une
exception d'adressage (dpend de la mise en oeuvre)
on peut convertir de volatile vers non volatile

6.1.19.

oprateurs pointeurs sur membres

* lie son second argument, de type pointeur sur un membre de T, son premier argument, de type T ou d'une classe
dont T est classe de base accessible
id pour ->* avec un pointeur
le rsultat est un objet ou une fonction
si c'est une fonction, il peut tre utilis pour un appel
class A{
int f1(void *);
int f2(void *);
};
typedef int (A::*Afptr)(void *);
main(){
Afptr pt = A::f1;
A a;
(a.*pt)(0);
}

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

71

Cours de programmation

6.1.20.

Laurent Henocque

Le langage C++

oprateurs de multiplication

oprandes de * et / de type arithmtique


oprandes de % (reste) de type entier
a / 0 et a % 0 ont une valeur indfinie
(a/b)*b + a%b vaut a

6.1.21.

oprateurs d'addition

arithmtique classique et arithmtique des pointeurs :


addition pointeur + entier et entier + pointeur
soustraction pointeur - entier
soustraction pointeur - pointeur (valable au sein d'un tableau, jusqu' la premire position au bout du tableau, sinon
indfini

6.1.22.

oprateurs de dcalage

le dcalage vers la gauche s'accompagne d'un remplissage par des 0 droite


le dcalage vers la droite est garanti remplir gauche par des 0 si le nombre dcaler est de type non sign, ou bien
s'il est positif. Sinon danger
le rsultat est indfini si l'oprande droit est plus grand que le nombre de bits de l'oprande gauche une fois promu
unsigned int bit1mask = 1U<<1;
unsigned int bit2mask = 1U<<2;

6.1.23.

oprateurs relationnels < > <= >=, et ==, !=

les arguments doivent tre de type arithmtique ou pointeur


le rsultat est de type int, valant 0 ou 1
tout pointeur peut tre compar (une expression constante valuant ) 0
tout pointeur peut tre compar un void *
des pointeurs sur des objets ou des fonctions de mme type peuvent tre compars
si deux pointeurs sur des membres non statiques d'un mme objet sont compars, et s'ils ne sont pas spars par un
label spcificateur d'accs, le pointeur sur celui dclar en dernier est plus grand que l'autre
deux pointeurs sur des membres donnes d'une mme union sont gaux
deux pointeurs sur un mme objet sont gaux
dans un tableau, le pointeur sur l'objet de plus grand index est le plus grand
les oprateurs == et != sont identiques mais de prcdence plus faible
des pointeurs sur membres de mme type peuvent tre compars

6.1.24.

oprateurs logiques bit bit

ces oprateurs ne s'appliquent que sur des arguments de type entier


le rsultat est la fonction logique bit bit des arguments

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

72

Cours de programmation

Laurent Henocque

ils servent notamment pour raliser des masques

6.1.25.

Le langage C++

2 4, 3 & 2

oprateurs logiques && et ||

ils associent et ils valuent de gauche droite


ils n'valuent l'argument droit que si c'est ncessaire
les oprandes ne sont pas ncessairement de mme type mais doivent tre de type arithmtique ou pointeur

6.1.26.

oprateur conditionnel a ? b : c

il associe et il value de gauche droite (seulement l'un des deux derniers)


le type de l'expression (et du rsultat) est un type commun aux oprandes 2 et 3, s'il existe, aprs conversions possibles
si les arguments 2 et 3 sont des l_values, le rsultat aussi

6.1.27.

affectation =, += etc

l'oprande gauche doit tre une l_value modifiable, qui donne son type au rsultat et l'expression
l'oprande droit doit pouvoir tre converti dans le type attendu gauche avant affectation (pas les numrations)
T* = T*const //OK
T*const = T* // impossible
ptr [sur membre] = ptr [sur membre] //OK
ptr [sur membre] = 0; //OK
T, volatile T = const T, volatile T; //OK
class X = ; // dfini par X& X::operator=(const X&);
class X = ; // sinon affectation membre membre
class X = class Y; // OK ssi Y hrite de X de faon non ambigu
T& = ; // affecte l'objet rfr

X op= Y est identique X = X op (Y), sauf que X n'est valu qu'une seule fois

6.1.28.

oprateur d'enchanement (virgule)

virgule associe et value de gauche droite, et la valeur de l'expression gauche est abandonne
le type est celui de l'expression de droite

6.1.29.

expressions constantes

elles sont ncessaires dans certaines situations : case, bornes de tableaux, longueurs de champs de bits, initialiseurs de
types numrs
elles sont connues et values la compilation, remplaces par leur valeur
elles consistent en :
des littraux
des expressions sizeof
des numrs

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

73

Cours de programmation

Laurent Henocque

Le langage C++

des valeurs const de types entiers initialises par des expressions constantes

6.1.30.

propos de conversion de types

la notation C habituelle pour la coercition est possible : int i = (int) 3.2;


on utilise la notation fonctionnelle pour les types ayant un nom simple
la slection de membre a une prcdence plus forte que la coercition, ce qui rend la notation fonctionnelle plus
agrable :
((complex)n).re;

//cast

complex(n).re;

//fonction

quand un cast explicite n'est pas ncessaire, il faut l'enlever


la conversion explicite de pointeur vers int, ou de pointeur vers un autre pointeur est non portable (char * peut diffrer
de int *, et de int)
seules sont garanties les conversions de pointeur vers void * et retour (on peut donc passer par void *)

6.1.31.

propos de mmoire dynamique

new, delete, delete [] pour les tableaux


l'oprateur delete connat la taille de l'objet devant tre dsallou par le compilateur, sauf dans le cas des tableaux.
#include <stddef.h>
void * operator new(size_t);
void operator delete (void *);
void out_of_memory(void){
cerr << "plus de memoire" << endl;
exit (1);
}
int main(){
set_new_handler(&out_of_memory);
char *p=new char[10000000000000000000000];
cout << "cette trace n'a aucune chance" << endl;
}

pas d'initialisation automatique zro


new retourne 0 en cas d'impossibilit d'allocation, mais termine sans erreur

6.2. Syntaxe des instructioNS


instruction :
dclaration
{ liste d'instructions }

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

74

Cours de programmation

Laurent Henocque

Le langage C++

expression ;
if ( expression ) instruction
if ( expression ) instruction else instruction
switch (expression) instruction
while (expression) instruction
do instruction while (expression)
for (instruction_for expression ; expression ) instruction
case expression-constante : instruction
default : instruction
break;
continue;
return expression ;
goto identificateur ;
indentificateur : instruction
instruction for :
dclaration
expression

6.2.1. slection et contrle


C++ nenrichit pas les instructions C : if, switch, case, while, do while, for, goto, label
il ny a pas de type boolen pour les tests : la valeur 0 signifie false, et toute autre valeur signifie true
les oprateurs <=, < etc. renvoient 0 si faux, et 1 sinon
toute valeur entire ou pointeur peut donc tre utilise dans un test

6.2.2. if
l'utilisation de if se fait comme en C. Certains tests peuvent tre supprims en utilisant l'oprateur de slection
conditionnelle (?:), mme en l_value, ce qu'ignorent beaucoup de programmeurs :
if (a<b) {
max = b ;
b_used = 1;
} else {
max = a;
a_used = 1;
}

devient :
max = (a<b) ? b : a;

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

75

Cours de programmation

Laurent Henocque

Le langage C++

((a<b) ? b_used : a_used) = 1;

6.2.3. switch
switch est plus performant que if lorsqu'une expression est compare des constantes
le compilateur peut informer sur des oublis lorsque lon utilise des types numrs

6.2.4. goto
goto est utile dans des programmes gnrsautomatiquement (analyseurs syntaxiques)
utile dans des cas particuliers de besoin en performance dans une boucle temps rel par exemple
galement et surtout pour sortir dune boucle de faon brutalelors en conomisant des tests
void f(int a){
int i, j;
for (i=0 ; i<n ; i++)
for (j=0 ; j<n ; j++)
if (nm[i][j] == a) goto found;
// not found
// ... return
found:
// found
}

6.3. commentaires et indentation


un commentaire dcrit le programme : sens, correct, complet, ( jour)
chaque fois que possible, un commentaire doit tre dcrit par le langage
notamment, un assert est prfrable tout commentaire
lindentation peut tre prise en charge automatiquement par un indenteur

6.4. exercices
6.4.1. exercice
crire linstruction while quivalente linstruction for suivante :
for (i=0

; i<maxlen ; i++)

if (input[i] == ?) quest++;

utiliser un pointeur comme variable de contrle


Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

76

Cours de programmation

Laurent Henocque

Le langage C++

6.4.2. exercice
parenthser les expressions suivantes :
a = b + c * d << 2 & 8
a & 077 != 3
a == b || a == c && c < 5
c = x != 0
0 <= i < 7
f(1,2) + 3
a = -1 + + b -- - 5
a = b == c++
a =

b= c =0

a[4][2] *= * b ? c : * d * 2
a-b,c=d

6.4.3. exercice
trouvez 5 constructions C++ diffrentes dont la signification est indfinie
trouvez dix exemples de code C++ non portable
que se passe til lors dune division par zro sur votre systme
que se passe til lors de dpassement de capacit par ajout excessif, retrait excessif

6.4.4. exercice
parenthser correctement les expressions suivantes :
*p++
*--p
++a-(int*)p->m
*p.m
*a[i]

6.4.5. exercice
crire strlen, strcpy, strcmp

6.4.6. exercice
observer les ractions du compilateur aux erreurs suivantes
void f(int a, int b) {

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

77

Cours de programmation

Laurent Henocque

Le langage C++

if (a = 3) // ...
if (a&077 == 0) //...
a := b+1;

imaginez des erreurs plus simples et voyez comment le compilateur ragit

6.4.7. exercice
crire la fonction cat qui concatne deux chanes
crire reverse, une fonction qui renverse une chaine

6.4.8. exercice
crire un programme qui limine les commentaires C++ dans un programme
attention aux chanes de caractres

6.4.9. exercice
que fait le programme suivant , et pourquoi lcrire :
void send (register*to, register *from, register count){
register n=(count+7)/8;
switch (count%8){
case 0 : do {

*to++ = *from++;

case 7:

*to++ = *from++;

case 6:

*to++ = *from++;

case 5:

*to++ = *from++;

case 4:

*to++ = *from++;

case 3:

*to++ = *from++;

case 2:

*to++ = *from++;

case 1:

*to++ = *from++;
} while (--n>0);

}
}

6.4.10.

exercice

crire la fonction atoi qui prend une chane de caractres et retourne un int
octal et hexadcimal
constantes caractres
crire la fonction inverse itoa

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

78

Cours de programmation

7.

Laurent Henocque

Le langage C++

Surcharge d'oprateur
7.1. introduction
C++ permet de dfinir des oprateurs pour toutes les classes dfinies par un programmeur
Exemples :
le type complexe, pour lequel on dfinit a + b,
le type vecteur, pour lequel on dfinit loprateur []

7.2. fonctions oprateurs


+ - * / % ^^& | !
= < > += -= *= /= %= ^= &=
|= << >> >>= <<= == != <= >= &&
|| ++ -- ->* , -> [] () new delete

Attention : il est impossible de changer la prcdence des oprateurs, ni leur sens d'appariement. "=" associe de droite
gauche, alors que "<<" associe de gauche droite. Ce comportement est fig.
Attention : il est impossible de changer la syntaxe des expressions (nouveaux oprateurs, ou bien modification de
binaire en unaire par exemple)
le nom dune fonction oprateur est operator suivi du symbole adquat, avec ou sans espace
operator ==

7.2.1. oprateurs binaires et unaires


binaire : dfinissable comme fonction membre un argument, ou comme fonction globale deux arguments
unaire : dfinissable comme fonction membre sans argument, ou comme fonction globale un argument
a@b sinterprte soit comme a.operator@(b), soit comme operator@(a,b)
@a sinterprte soit comme a.operator@(), soit comme operator@(a)
Lorsque les deux sont dfinis, le choix est fait avec les rgles habituelles
a@ (postfixe) sinterprte par a.operator(int) ou par operator@(a,int)

7.2.2. spcification pr dfinie des oprateurs


=, [], () et -> doivent tre des fonctions membres non statiques. += nest pas dduit automatiquement de + et de =
=, &, et , (enchanement) ont une valeur pr dfinie. Ils peuvent tre rendus inaccessibles en les dclarant privs dans
la classe.

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

79

Cours de programmation

Laurent Henocque

Le langage C++

7.2.3. oprateurs et types utilisateur


Une fonction oprateur doit soit tre membre dune classe utilisateur, soit prendre une classe utilisateur en paramtre.
C++ est donc extensible, mais pas modifiable, sauf pour les oprateurs pr dfinis par dfaut : =, & et ,.
La commutativit dun oprateur nest pas connue du compilateur. On doit dclarer deux fonctions pour lobtenir.

7.3. conversions de types dfinis par le programmeur


On ne doit normalement pas dfinir autant doprateurs amis que le ncessitent des conversions automatiques voulues,
grce aux oprateurs de conversions de types
class complex {
double re, im;
public :
complex(double r, double i) re(r), im(i) {};
friend complex operator+(complex,complex);
friend complex operator+(double,complex); // inutile
friend complex operator+(complex,double); // inutile itou
...
}

7.3.1. conversion par constructeurs


class complex {
double re, im;
public :
complex(double r, double i = 0) re(r), im(i) {};
friend complex operator+(complex,complex); // suffit maintenant
...
}

7.3.2. un principe de mise en oeuvre


On dfinit en gnral les oprateurs de type *= en premier et on en drive *:
matrix& matrix::operator*=(const matrix& a)
{
//..
return *this;

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

80

Cours de programmation

Laurent Henocque

Le langage C++

}
matrix matrix::operator*(const matrix& a, const matrix& b)
{
matrix prod = a;
prod *=b;
return prod;
}

Une conversion utilisateur est automatiquement applique si elle est unique.


Tout objet construit par lappel explicite dun constructeur est automatique et sera dtruit ds que ncessaire.

7.3.3. oprateurs de conversion


La conversion par constructeur est limite :
pas de conversion vers les types de base
pas possible de convertir un type nouveau en un type ancien sans modifier lancien
pas possible davoir un constructeur un seul argument sans avoir aussi de facto une conversion (toutefois le mot clef
explicit avant le constructeur permet de bloquer ce type de conversion automatique)
class tiny {
char v;
void assign (int i) { assert (! (i & 63)); v = i;}
public :
tiny(int i) { assign (i);}
tiny (const tiny &t) { i = t.v;} // ou utilisation de assign
tiny& operator=(const tiny& t) {i=t.v; return *this;}
tiny& operator=(const int& i) {assign (i); return *this;}
operator int() {return v;} // conversion en int
};
main(){
tiny c1 = 2;
tiny c2 = 62;
tiny c3 = c2 - c1;
tiny c4 = c3; // pas de vrif
int i = c1 + c2;
c1 = c2 + 2*c1; // erreur
c2 = c1 - i;

// erreur

c3 = c2; // pas de vrif


}

7.3.4. oprateurs de conversion (2)


exemple officiel (mais il nest pas bon en gnral de perdre de linformation lors dune conversion)

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

81

Cours de programmation

Laurent Henocque

Le langage C++

while (cin >> x) cout << x; // conversion implicite de cin en int (son tat)

Il est conseill de ne pas abuser des conversions, qui rendent les programmes difficiles lire.

7.3.5. ambigut
Le compilateur ne ralise quun niveau de conversion automatique. Pour en avoir plusieurs, il faut les expliciter.
Les conversions utilisateur ne sont utilises que sil est impossible de faire autrement. Si une conversion standard
permet de traiter lappel, elle est utilise.

7.4. littraux
On ne peut dfinir des littraux de classes.
Mais on peut invoquer les constructeurs pour des types de base.
Ainsi : complex(1,2) peut tre considr comme une constante littrale de la classe complex.
Si le constructeur est inline, une expression lincluant ne comportera pas dappel de fonction supplmentaire.

7.5. gros objets


Pour viter des copies de gros objets, les oprateurs prennent en gnral des arguments rfrence. Voir lexemple
prcdent de la classe matrix.
On ne peut retourner une rfrence cause dun problme dallocation mmoire. Il sera donc ncessaire de procder
une copie de lobjet retourn. (On ne peut pas retourner une rfrence une variable locale static, car un oprateur peut tre
utilis plusieurs fois dans une mme expression).

7.6. affectation et initialisation


Lorsquun objet alloue de lespace lors de son initialisation, il faut veiller redfinir un constructeur par copie, et un
oprateur daffectation, sous peine derreurs alatoires lors de la destruction (plusieurs appels de delete sur le mme pointeur)
exemple de string

7.7. indexation
Largument supplmentaire (index) de loprateur dindexation peut tre de nimporte quel type. On peut lutiliser
pour des dictionnaires, dont lindex est directement une chane de caractres.

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

82

Cours de programmation

Laurent Henocque

Le langage C++

7.8. oprateur dappel de fonction


Cet oprateur peut servir pour appeler une fonction de lobjet qui prime sur toutes les autres.
operator()() doit tre une fonction membre.
expression (liste dexpression) est interprt comme un oprateur binaire ayant expression comme premier argument,
et liste dexpression comme deuxime argument.
X obj;
obj.fn_toujours_utilisee(); // remplac par :
obj();

7.9. indirections
l'oprateur -> est dfini comme un oprateur postfixe unaire.
class Ptr {
//
X* operator->();
}
void f (Ptr p){
p->m = 7; // (p.operator->())->m = 7;

Cet oprateur sert calculer un pointeur sur un objet. Lorsqu'on utilise la version habituelle de la notation ->, un nom
de membre est obligatoire aprs.
X* x = p->; // erreur
X* x = p.operator->(); // ok

On peut utiliser -> pour crer des pointeurs intelligents.


class X {
Y *p;
int compteur;
public :
X(Y* _p):p(_p),compteur(0){};
Y* operator->()

{return p;

Y& operator*()

{return *p; compteur++;}

compteur++;}

Y& operator[](int i)

{return p[i];compteur++;}

};

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

83

Cours de programmation

Laurent Henocque

Le langage C++

7.10. incrmentation et dcrmentation


On peut surcharger les oprateurs ++ et -- pour avoir un contrle de bornes automatique.
class CheckedPtrToT {
T* p;
int position;
int taille;
//
T *operator++();

//prfixe

T *operator++(int);
T *operator--();

//prfixe

T *operator--(int);
T &operator*();

//postfixe
//postfixe

//prfixe

}
void f(T t) {
T v[200];
CheckedPtrToT p(v,200);
p--;

// p.operator--(1);

*p = t;
++p;

// p.operator*() = t;

// erreur

// p.operator++();

*p = t;

// p.operator*() = t;

// ok

7.11. une classe chane de caractres


class string {
struct srep {
char *s;
int n;
srep() {n=1;}
}
srep *p;
public :
string (const char *s){
p = new srep;
p->s= new char[strlen(s)+1];
strcpy(p->s,s);
}

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

84

Cours de programmation

Laurent Henocque

Le langage C++

string ();{
p = new srep;
}
string (const string &from){
from.p->n++;
p = from.p;
}
// et les oprateurs ==, !=, =
}

7.12. amies et membres


On a souvent le choix entre dfinir des fonctions membres ou des fonctions globales prenant des arguments rfrence.
Ex m.inv(), ou bien inv(m) pourinverser une matrice m.
Toutes choses gales par ailleurs, on choisit toujours un membre. Les rfrences montrent de faon moins lisible que
l'objet peut tre modifi.
Une fonction modifiant l'tat d'un objet doit tre un membre, ou bien une fonction globale prenant un argument
rfrence non const.
Par contre, une fonction ncessitant une conversion de type pour tous ses arguments ne peut tre que globale. En effet
une fonction membre ne convertit jamais le type de l'objet auquel elle s'applique. Cette considration guide aussi lors de la
dfinition des oprateurs qui peuvent aussi bien tre des globales que des membres. On utilise une fonction membre si l'on
veut protger le paramtre "principal" contre une coercition automatique non voulue.
class X {
//
X(int);
int m1();
int m2()const;
friend int f1(X&);
friend int f2(const X&);
friend int f3(X);
}
1.m1();

// erreur : X(1).m1() non essay

1.m2();

// erreur : X(1).m2() non essay

f1(1);

// erreur : impossible

f2(1);

// ok f2(X(1))

f3(1);

// ok f3(X(1))

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

85

Cours de programmation

Laurent Henocque

Le langage C++

7.13. avertissement
L'utilisation abusive des oprateurs peut conduire des programmes illisibles.
L'utilisation d'oprateurs contre emploi aussi :
int operator+(int a, int b) {return a - b;}

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

86

Cours de programmation

8.

Laurent Henocque

Le langage C++

Templates
8.1. introduction

Les patrons servent dfinir des types, ou des oprations, utilisant ou s'appliquant des types fournis comme
paramtres
Ce mcanisme s'appelle la gnricit
On peut ainsi dfinir des listes de "n'importe quoi", et plus gnralement des classes "conteneurs de n'importe quoi".
L'objectif est de garder la vrification de type, et les performances
Le principe consiste dcrire des schmas de programmes, qui peuvent ensuite tre complts par les fragments
manquants, la demande.
La simple utilisation d'un patron provoque la cration des programmes ncessaires.

8.2. un patron simple : patron de classe


template<class T>
class stackOf {
T* v;
T* p;
int sz;
public:
stack(int s) {v = p = new T[sz = s];}
stack() {delete[] v;}
void push(T a) {*p++ = a;}
T pop() {return *--p;}
int size() const {return p-v;}

8.2.1. utilisation
Ce template s'utilise ensuite ainsi :
stackOf<char> sc(100);

// pile de 100 caractres max

stackOf<int> si(30);

// pile de 30 entiers max

On peut galement avoir des classes gnriques dont les fonctions ne sont pas inline, mais les compilateurs balbutient
encore un peu dans ce domaine

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

87

Cours de programmation

Laurent Henocque

Le langage C++

template<class T> void stack<T>::push(T a) { *p++ = a;)


template<class T> void stack<T>::stack(int s) {v=p=new T[sz=s];}

Noter dans le second cas que la rptition du paramtre de type est optionnelle

8.3. listes gnriques


8.3.1. liste intrusive
struct slink {// classe lien
slink* next;
slink() : next(0){}
slink(slink* n):next(n){}
};
struct list {// classe de gestion de liens
//
public:
void insert (slink *);

//ajout en tte

void append (slink *);

//ajout en fin

slink *get();

//dtruit et retourne le dbut

};
class name : public slink {
char *n;
//
};
template<class T>
class listOf : private list {
public:
void insert(T* a) {list::insert(a);}
T *

get() {return (T*) list::get();}

}
void f(char *s){
listOf<name> ln;
ln.insert(new name(s));
name *n = ln.get();

//pas de coercition ncessaire

avantages :
typage sr

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

88

Cours de programmation

Laurent Henocque

Le langage C++

pas de surcot
pas de rplication de code
possibilit de cacher le code source de list l'utilisateur
une liste intrusive est optimale en temps et en mmoire

inconvnients :
pas possible de raliser des listes croises

8.3.2. une liste non intrusive


template<class T>
struct Tlink : public slink {
T info;
Tlink(const T& d):info(d){}
};
template<class T>
class slist : private list {
void insert(const T& a) {list::insert(new Tlink<T>(a));}
void append(const T& a) {list::append(new Tlink<T>(a));}
T get();
};
template<class T>
slist<T>:get() {
Tlink<T> *l = (Tlink<T> *)list::get();
T d=l->info;
delete l;
return d;
}
void f(int i) {

//pas de sous classe de slink

slist<int>l1;
slist<int>l2;
l1.insert(i);
l2.insert(i); //le mme i sur deux listes
}

8.3.3. avantages / inconvnients


la liste intrusive est avantageuse en performance (pas d'allocation ni de copie)

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

89

Cours de programmation

Laurent Henocque

Le langage C++

8.3.4. principe
Ne pas utiliser de rfrences comme arguments de templates : ex : slist<int&>
Cela peut provoquer des erreurs l'expansion du patron

8.3.5. listes de pointeurs


//listes de pointeurs
template<class T>
class Splist : private Slist<void *> {
public :
void insert (T* p) {Slist<void *>::insert(p);}
T*

get() {return (T *) Slist<void *>::get();}

8.3.6. instance de template comme argument de template


Une instance de template est un type "normal". On peut donc utiliser un tel type comme paramtre d'un template.
typedef Slist< Slist<int> > ints; // remarquer les espaces

8.3.7. une implantation effective des listes


class list {
slink *last;

//liste finie code de faon circulaire

public:
void insert(slink *a);
void append(slink *a);
slink *get();
void clear(){last = 0;}
list()last(0){}
list(slink *a)last(a->next=a){}
friend class listIter;
}
typedef void (*PFV) (const char *);
PFV setListErrorHandler(PFV f) {
PFV old = listErrorHandler;
listErrorHandler = f;
return old;
}
void defaultListErrorHandler(const char *s) {
assert(0);

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

90

Cours de programmation

Laurent Henocque

Le langage C++

}
PFV listErrorHandler = &defaultListErrorHandler;

8.3.8. itration
class listIter {
slink *e;
list *l;

//lment courant
//liste courante

public:
listIter(list&_l):l(&_l),e(l->last){}
inline slink* operator()();
}
slink* listIter::operator()(){
slink *ret = e ? e->next : 0;
if (ret == l->last) e = 0;
return ret;
}
template<class T> class IslistIter; //dclaration pralable
template<class T> class Islist {

//intrusive

friend class IslistIter;


//
}
template<class T> class SlistIter; //dclaration pralable
template<class T> class Slist {

//non intrusive

friend class SlistIter;


//
}

8.3.9. itrateurs (2)


template<class T>
class IslistIter : private listIter { //hrite de classe de base
public:
IslistIter(Islist<T>& l):listIter(l){}
T* operator()(){return (T*) listIter::operator()();}
}
template<class T>
class SlistIter : private listIter { //hrite de classe de base

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

91

Cours de programmation

Laurent Henocque

Le langage C++

public:
IslistIter(Islist<T>& l):listIter(l){}
T* operator()(){
return ((Tlink<T>*)listIter::operator()())->info;
}
}
void f(name *p){
Islist<name> l1;
Slist<name> l2;
l1.insert(p);
l2.insert(p);
IslistIter<name> it1(l1);
const name *p;
while (p = it1()) { // do something }
}

8.4. fonctions gnriques globales


template<class T> void sort(vect<T>&) {
/* trie en ordre croissant */
unsigned n = v.size();
for (int i = 0 ; i<n-1 ; i++)
for (int j = n-1 ; i<j ; j--)
if (v[j]<v[j-1]) swap (v[j],v[j-1]);
}
//version particulire pour char * car < ne fonctionne pas
void sort(vect<char*>&) {
/* trie en ordre croissant */
unsigned n = v.size();
for (int i = 0 ; i<n-1 ; i++)
for (int j = n-1 ; i<j ; j--)
if (strcmp(v[j],v[j-1])<0) swap (v[j],v[j-1]);
}
void f(vect<int>&vi, vect<char*>&vc, vect<string>vs) {
vi.sort();
vc.sort();
vs.sort();
}

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

92

Cours de programmation

Laurent Henocque

Le langage C++

8.4.1. exemple
On peut aussi implanter une classe de vecteurs triables
template<class T> void sort(sortableVect<T>&) {
/* trie en ordre croissant */
unsigned n = v.size();
for (int i = 0 ; i<n-1 ; i++)
for (int j = n-1 ; i<j ; j--)
if (v.lessThan(v[j],v[j-1])) swap (v[j],v[j-1]);
}
template<class T> sortableVect:public vect, public comp<T> {
public:
sortableVect(int s):vect(s){}
}
template<class T> class comp {
static int lessthan(T&a,T&b) {return a<b;}
}
class comp<char *> {
static int lessthan(T&a,T&b) {return strcmp(a,b)<0;}
}

8.5. rsolution de la surcharge des fonctions gnriques


cration d'une nouvelle fonction pour chaque type argument
pas de conversion des types

8.5.1. surcharge explicite possible :


template<class T> T max (Ta, Tb) { return a<b ? b : a;}
int max(int a, int b) {}

8.5.2. templates plusieurs arguments


template<class T, class U> T max(T a, U b) {}

8.6. arguments de template


pas besoin d'tre un argument de type :

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

93

Cours de programmation

Laurent Henocque

Le langage C++

template<class T, int sz> class buffer {


T v[sz];
}

c'est utile dans le cas o la taille est connue la compilation


chaque argument de template de fonction doit figurer dans le type d'un des arguments de la fonction. Cela permet la
slection et la gnration de la bonne fonction lors d'un appel.
Deux classes gnres par template sont gales si leurs arguments de cration sont identiques modulo un typedef,
l'valuation de constantes, etc..

8.7. drivation et templates


Une classe exprime ce qui est commun dans la reprsentation et les interfaces d'appel, et un template exprime ce qui
est commun aux types utiliss comme arguments
Deux classes gnres par template ne sont pas dans une relation d'hritage particulire

8.7.1. mise en oeuvre via les templates


certaines classes ncessitent d'allouer de la mmoire
on peut contrler cette allocation par l'utilisation d'un template
template<class T, classA> class controlled_container :
public container<T>, private A {
void

f() {
T *p = new(A::operator new(sizeof(T))) T;

8.8. un tableau associatif


template<class V, class K> class map {
public :
V& operator[](const K &k) { // trouver le V partir du K
// grce <, et ==

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

94

Cours de programmation

9.

Laurent Henocque

Le langage C++

Gestion des exceptions


9.1. gestion des erreurs

En cas de situation exceptionnelle (i.e. sortant du cadre prvu par l'algorithme), le programmeur se trouve devant
plusieurs alternatives, nonquivalentes en cot de conception et de codage :
1 : terminer le programme en indiquant le lieu de l'erreur (assert())
2: retourner une valeur reprsentant l'erreur, qui doit tre propage, et traite, par toutes les fonctions appelantes
3: continuer dans un tat invalide (avec ou non affichage de message)
4: provoquer un "longjump" jusqu' un point o l'erreur peut tre traite (et un tat valide reconstruit)
C++ offre une solution lgante et performante au problme de la prise en compte des exceptions

9.2. discrimination et nommage des exceptions


dclaration d'intercepteurs :
class vector {
int sz;
int *v;
//
class range{
int index;
range(int i) : index(i){}
};
class size{};
vector(int i) : sz(i) { v = new int[i]; if (!v) throw size();}
int& operator[](int i) { if (i>=sz) throw range(i); return v[i];
}

9.2.1. utilisation
void f()
{
try {
g();
} catch (vector::range r) {
cout << "bad index" << r.index << endl;

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

95

Cours de programmation

Laurent Henocque

Le langage C++

} catch (vector::size) { // une autre erreur par exemple


}
}

9.2.2. Les interceptions peuventtre imbriques


9.2.3. On peut utiliser des templates de classes d'exceptions
Chaque classe gnre aura son propre gestionnaire d'exceptions
template<class T> class allocator {
class exhausted{};
};
void f() {
try {
// allocate for some room
} catch (allocator<int>::exhausted)

} catch (allocator<char>::exhausted)

}
}

La classe exception peut aussi tre globale, i.e. non intgre dans la classe qui provoque l'exception

9.2.4. regroupement d'exceptions


On peut grouper le traitement des exceptions
enum matherr {overflow, underflow, zerodivide, etc...}
try { //... }
catch (matherr m) {
switch (m) case overflow: break; case
}

On peut utiliser l'hritage dans des classes d'exceptions et profiter des fonctions virtuelles
C'est utile car cela permet de traiter toutes les exceptions, y compris celles venir
class matherr {};
class overflow : public matherr {};
class underflow : public matherr {};
class zerodivide : public matherr {};
try { }
catch (overflow) {

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

96

Cours de programmation

Laurent Henocque

Le langage C++

// tout overflow ou sous classe de overflow


} catch (matherr) {
// tout matherr qui n'est pas overflow
}

9.2.5. Exceptions drives


Une exception est gnralement intercepte par un gestionnaire d'une classe de base
Pour disposer de toute l'information relative l'exception exacte, il faut utiliser une rfrence
try {}
catch (errtype &e){}

On peut relancer une exception


try {}
catch (errtype &e){
if (cantdoit) throw; //relance l'exception exacte d'origine
}

Les gestionnaires sont essays dans l'ordre, et cet ordre est donc opportun
Le compilateur connat la hirarchie des classes et peut donc signaler des erreurs : exception masque et donc jamais
gre par exemple.
catch(...)signifie : intercepter n'importe quelle exception
try { // qqchose
} catch (...){
// autre chose
throw; //relance l'exception exacte d'origine
}

9.3. acquisition de ressources


Le mcanisme de throw garantit que l'appel des destructeurs des variables automatiques est correctement effectu
On peut tirer parti de ce mcanisme pour acqurir des ressources dans des constructeurs,
et librer ces ressources dans les destructeurs.
Exemple de la trace

9.3.1. constructeurs et destructeurs


Un objet n'est pas considr construit tant que son constructeur n'est pas termin.
Un constructeur n'est termin que pourvu que tous les sous objets ont t construits.

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

97

Cours de programmation

Laurent Henocque

Le langage C++

Le destructeur ne sera appel que si l'objet est construit.


class X {
filePtr aa;
lockPtr bb;
X(const char *x, const char *y) : aa(x), bb(y) {}
};

Si le constructeur de bb lance une exception, celui de aa ayant t termin, le destructeur de aa sera appel, mais pas
celui de bb.

9.3.2. acquisition de ressources = initialisation


Un constructeur bien pens obit au principe : l'acquisition de ressources est une initialisation.
Si l'acquisition choue, le constructeur choue.
struct X{
int *p;
X(int s) {p = new int[s]; init()};
X() {delete[] p;}
}; // si init lance une exception, p n'est jamais libr

variante sre :
template<class T>class arrayOf{
T *p;
arrayOf(int s){p = new T[s];}
arrayOf(){delete[] p;}
operator T* (){return p;}
}
struct X{
arrayOf<int> p;
X(int s) : p(s){init()};
X() {}
}; // si init lance une exception, p est libr

Le code d'un constructeur n'est pas appel si new ne parvient pas allouer assez d'espace pour l'objet.

9.3.3. puisement de ressources


Lors de l'incapacit obtenir une ressource, on peut prvenir le programme appelant (en appelant une fonction de
l'appelant), ou encore lancer une exception.
On combine en gnral les deux approches de la faon suivante (exemple de l'oprateur new):
#include <stdlib.h> //pour size_t

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

98

Cours de programmation

Laurent Henocque

Le langage C++

extern void * _last_allocation;


extern void * operator new(size_t size) {
void * p;
while (0 == (p = malloc(size))) {
if (_new_handler) (*_new_handler)();
else return 0;
}
return _last_allocation = p;
}

9.3.4. puisement de ressources


// pour l'utiliser :
void my_new_handler() {
if (can_get_some_mem()) return;
throw MemoryExhausted;
}
// et dans une fonction :
void (*oldnh)() = set_new_handler (&my_new_handler);
try {}
catch (MemoryExhausted) {}
catch (...){
set_new_handler (oldnh);

//remet

throw;

//relance

}
set_new_handler (oldnh);

//remet

9.3.5. rgle lors de l'utilisation de callbacks


Il est bon d'changer aussi peut de paramtres que possible avec les callbacks, pour viter de rendre interdpendants
des programmes qui n'ont pas de rapport.

9.4. exceptions qui ne sont pas des erreurs


Terminer lgamment une recherche hautement rcursive (dans un arbre par exemple)
Sortir d'une boucle infinie parcourue un grand nombre de fois
Attention la lisibilit des programmes rsultants, car les exceptions sont un mcanisme de contrle moins bien
structur que les autres.

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

99

Cours de programmation

Laurent Henocque

Le langage C++

9.5. spcification d'interface


On peut dclarer les exceptions pouvant tre lances par une fonction :
void f() throw (x2,x3,x4);
void f() throw (x2,x3,x4)
void f() {
try { /* code */ }
catch (x1) {throw;}
catch (x2) {throw;}
catch (x3) {throw;}
catch (...) {unexpected ();} // terminate() puis abort()
}
int g() throw();//ne peut lancer aucune exception
int h();

//peut lancer toutes les exceptions

9.5.1. utilisation de set_unexpected


On peut utiliser set_unexpected pour redfinir le comportement par dfaut, et appeler throw quand mme pour relancer
une erreur qui ne figure pas dans l'interface de la fonction
typedef void (*PFV)();
PFV

set_unexpected (PFV);

class STC {
PFV old;
STC(PFV f) : old(set_unexpected(f)) {}
STC(){set_unexpected(old);}
};
void rethrow () { throw;}
f_qui_laisse_passer()
{
STC(&rethrow);
f();
}

9.6. exceptions non interceptes


Une exception non intercepte provoque un appel terminate
PFV set_terminate (PFV);
Un appel terminate est suppos ne pas retourner son appelant.

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

100

Cours de programmation

Laurent Henocque

Le langage C++

9.7. alternatives la gestion des erreurs


int f(args) {
try {
g(args);
} catch (x1) {// corrige, et ressaye
g(args);
} catch (x2) {// calcule et retourne un rsultat
return 2;
} catch (x3) {//relance
throw;
} catch (x4) {//change en une autre exception
throw y4;
} catch (x5) {corrige et continue la fonction
// du code
} catch (...) {traduit en une autre forme d'erreur
errno = EBZCLDF;
terminate();
}

9.8.

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

101

Cours de programmation

Laurent Henocque

Le langage C++

10. Flots
10.1. introduction
Les flots sont accessibles par l'interface "iostream.h"
Les noms de fonction sont surchargs pour tous les types souhaits
Des oprateurs sont dfinis (<< et >>)

10.2. sorties
put(cerr, "x = ");
put(cerr, x);
put(cerr, "\n");
// notation directe :
cerr << "x = " << x << "\n";
cout << a * b + c << "\n"; //prcdence assez basse de <<
class ostream : public virtual ios {
//
public :
ostream& operator<<(const char *);
ostream& operator<<(char);
ostream& operator<<(short i){return *this<<int(i);;
ostream& operator<<(int);
ostream& operator<<(long);
ostream& operator<<(float);
ostream& operator<<(double);
ostream& operator<<(const void *);//tous les pointeurs
};

10.2.1.

extension pour une classe utilisateur

class complex{
//
friend ostream& operator<<(ostream& o, complex&c);

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

102

Cours de programmation

Laurent Henocque

Le langage C++

}
ostream& operator<<(ostream& o, complex&c) {
return o << '(' << c.re << ',' << c.im << ')';
}
//utilisation :
main(){
complex c(1,2);
cout << "le complexe c vaut " << c << endl;
}

10.3. entres
class istream : public virtual ios {
//
public :
istream& operator<<(char *);
istream& operator<<(char&);
istream& operator<<(short&);
istream& operator<<(int&);
istream& operator<<(long&);
istream& operator<<(float&);
istream& operator<<(double&);
};
// ide du code :
istream& operator<<(T& tvar) {
//sauter les espaces
//lire un "T" dans tvar
return *this
}
int readints (Vector<int>& v){
for (int i = 0;i<v.size ; i++) {
if (cin>>v[i++]) continue; //converteur implicite en int
return i;
}
//autres traitements
}
//autres mthodes de la classe istream :
istream& get (char &c);

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

103

Cours de programmation

Laurent Henocque

Le langage C++

istream& get (char *s,int n, char fin = '\n');

10.3.1.

fonctions dfinies dans <ctype.h> :

int isalpha(char);
int isupper(char);
int islower(char);
int isxdigit(char);
int isspace(char);
int iscntrl(char);//0->31, 127
int ispunct(char);//aucun des prcdents
int isalnum(char);//alpha ou digit
int isprint(char);//affichable ascii ' ' .. ''
int isgraph(char);//alnum ou ispunct
int isascii(char);//compris entre 0 et 127
//toutes sauf isascii sont values par accs direct ds tableau
//donc plus efficaces que des comparaisons

10.3.2.

tats du flot

int eof() const, //fin de fichier


fail() const, //prochaine opration chouera, mais tat du flot correct (rien de perdu)
bad() const, //ouups
good() const //c'est dans ce seul tat que les oprations font quelque chose
class ios {
//
public :
enum io_state {goodbit=0, eofbit=1, failbit=2, badbit=4};
rdstate (); //retourne un ensemble de bits d'io_state
};

10.3.3.

entre de types utilisateur

Le second argument doit tre une rfrence. Exemple (simplifi)


istream& operator>>(istream& s, complex&c) {
//trois formats : re , (re) , (re,im)
{
double re = 0, im = 0;

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

104

Cours de programmation

Laurent Henocque

Le langage C++

char c = 0;
s >> c;
if ( c == '(' ) {
s >> re >> c;
if ( c == ',' ) {
s >> im >> c;
} // pas de else
if (c != ')') {
s.clear (ios::badbit); //
} else {
s.putback(c);
s >> re;
}
if (s) a = complex (re,im);
return s;
}

10.4. mise en forme


La classe ios de base permet de grer le lien entre un flot de sortie et un flot d'entre, ainsi que des informations lies
aux formats.
class ios {
//
public :
ostream *tie(ostream *s); //attache l'entre la sortie
ostream *tie()const;

//retourne la sortie lie

int width(int w);

//positionne le champ longueur

int width()const;

//retourne la longueur

char fill(char);
char fill()const;

//init le caractre de remplissage


//retourne ce caractre

long flags(long);
long flags()const;
long setf(long setbits, long field);
long setf(long);
long unsetf(long);
int precision (int);
int precision ()const;
void clear (int i = 0);
int rdstate()const, eof()const, bad()const, fail()const, good()const;

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

105

Cours de programmation

10.4.1.

Laurent Henocque

Le langage C++

liaison de flots

main () {
char s[50];
cout << "passwd :";
// grce tie, on peut omettre ici cout.flush();
cin >> s;
}
//
cin.tie(0); // permet de supprimer le lien

10.4.2.

champs de sortie

Le paramtre width (0 par dfaut) spcifie le nombre de caractres utiliser pour la prochaine (seulement) sortie
numrique ou de chane de caractres. La valeur 0 signifie : autant que ncessaire.
Le paramtre fill (espace par dfaut) est le caractre utilis pour combler
cout.width(4);
cout.fill(*);
cout << '(' << 12 << ')';
// produit :
(**12)

Width ne provoque pas de troncation

10.4.3.

tat du format

La classe ios maintient un paramtre (flags) dcrivant les diffrents paramtres de format.
class ios {
public :
enum {
skipws=01, //sauter les sparateurs en entre
//remplissage :
left=02,
right=04,

//avant la valeur
//aprs la valeur

internal=08,

//entre le signe et la valeur

//base entire :
dec = 020,
oct = 040,
hex = 0100,
showbase = 0200,

//montrer la base

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

106

Cours de programmation

Laurent Henocque

Le langage C++

showpoint = 0400, //affiche les zros aprs la virgule


uppercase = 01000, //majuscules
showpos = 02000,

//+ avant les positifs

scientific = 04000, //.dddddd Edd


fixed = 010000,

//ddd.dd

unitbuf = 020000, // aprs chaque sortie


stdio = 040000,

//aprs chaque caractre

};
};
//utilisation :
mystream.flags(mystream.flags() | ios::showbase);
//ou son quivalent :
mystream.setf(ios::showbase);

10.4.4.

gestion des bits exclusifs : base, remplissage, et flottants

cout.setf(ios::oct, ios::basefield); // met ce qu'il faut zro


cout.setf(ios::left, ios::adjustfield); // id
cout.setf(ios::fixed, ios::floatfield); // id
cout.setf(0, ios::floatfield);
cout.precision(4);

10.4.5.

// remet la valeur par dfaut

//chiffres aprs la virgule (6 par dfaut)

manipulateurs

Il est parfois ncessaire de procder des oprations sur les flots autres que des entres sorties, au milieu de ces
dernires. Ex :
cout << x; cout.flush(); cout<<y;

On peut procder ainsi :


typedef ostream& (*ostreamOp)(ostream&);
ostream& flush(ostream&);
ostream& operator<<(ostream&o, ostreamOp f) {
return f(o);
}
//ensuite :
cout << x << flush << y flush;
//un tel oprateur est dj dfini dans la classe ostream :
class ostream : public ios {

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

107

Cours de programmation

Laurent Henocque

Le langage C++

//
public :
ostream& operator<<(ostream& (*)(ostream&));

10.4.6.

manipulateurs de istream

istream &ws(istream& is) {return is.eatwhite();}


//
cin >> ws >> x;
//on peut tendre le mcanisme : cout << setprecision (4) << x;
Il faut construire un objet affichable
typedef ostream & (*Fostint)(ostream&, int);
class omanip_int {
int param;
Fostint fonct;
omanip_int (Fostint f, int p) : fonct(f),param(p) {}
friend ostream& operator<<(ostream&, omanip_int&)
};
omanip_int setprecision (int i) {
return omanip_int (&_set_precision, i);
}
ostream& operator<<(ostream& o, omanip_int& m) {
return m.fonct(o, m.param);
};
cout << x << setprecision(4) << y;

10.4.7.

utilisation de patrons

On peut dfinir un patron pour prvoir des structures de manipulateurs pour tous les types, autres qu'entiers :
template<class T> class OMANIP {
T i;
ostream& (*f)(ostream&,T);
public:
OMANIP(ostream& (*_f)(ostream&,T),T _i) : f(_f), i(_i){}

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

108

Cours de programmation

Laurent Henocque

Le langage C++

friend ostream& operator<<(ostream& o, OMANIP& m) {


return m.f(o,m.i);
}
};
ostream& precision (ostream& o, int i) {
o.precision (i);
return o;
}
OMANIP<int> setprecision (int i) {
return OMANIP<int>(&precision, i);
}
cout << precision(5) << x;

Les templates OMANIP, IMANIP et SMANIP (pour ios) sont dfinis dans <iomanip.h>

10.4.8.

intrt des structures de manipulateurs

Passer un objet plutt qu'appeler une fonction est une technique intressante, mme dans d'autres domaines que les
entres sorties.
Un objet est cr, puis pass n'importe o, et utilis comme une fonction.
Cela permet de partager les dtails d'excution entre l'appel et l'appelant.

10.4.9.

Manipulateurs d'entres sorties standard

ios& oct (ios&); // dec, hex


ostream& endl(ostream&); // ends, flush
istream& ws(istream&);

//saute les blancs

SMANIP<int> setbase(int); // et setfill, setprecision, setw


SMANIP<long> resetiosflags(long); // et setiosflags

10.4.10.

membres de ostream

On peut avoir un accs direct une position d'un fichier, ds qu'il est li un ostream :
class ostream : public virtual ios {
ostream& flush();
ostream& seekp(streampos);

//aller une position

ostream& seekp(streamoff,seek_dir);
streampos tellp();

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

109

Cours de programmation

Laurent Henocque

Le langage C++

};

seekp permet d'aller une position donne pour criture :


fout.seekp[10]; //li un file (char file[n]);
fout<<"@"; // crit @ dans file[10]

10.4.11.

membres de istream

On peut avoir un accs direct une position d'un fichier, ds qu'il est li un istream :
class istream : public virtual ios {
int peek(); // consulte le prochain caractre sans lire
istream& putback(char c);
istream& seekg(streampos);

//aller une position

istream& seekg(streamoff,seek_dir);
streampos tellg();
};

seekg permet d'aller une position donne pour lecture


les lettres p et g suffixes sont ncessaires car on peut crer une classe hritant la fois de ostream et de istream

10.5. fichiers et flots


#include <fstream.h>
les classes fstream, ofstream et ifstream drivent de iostream, qui est elle mme drive de istream et ostream.
Toutes les fonctionnalits de ces classes sont donc accessibles par fstream
class ios {
public :
enum open_mode {
in

1<<0;//ouvert en entre

out

1<<1;//ouvert en sortie

ate

1<<2;//ouvre et va a la fin

app

1<<3;//ajoute

trunc

1<<4;//tronque le fichier longueur 0

nocreate

1<<5;//choue si le fichier nexiste pas

noreplace

1<<6;//choue si le fichier existe

};
};
// exemple : copie d'un fichier dans un autre
main(int argc, char **argv){

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

110

Cours de programmation

Laurent Henocque

Le langage C++

assert(argc == 3);
ifstream inp(argv[1]);

assert(inp);

ofstream out(argv[2]);

assert(out);

char c;
while (inp.get(c) && out.put(c));
assert(inp.eof() && !out.bad());
}

10.5.1.

paramtrage

Un ofstream est ouvert en criture par dfaut, un ifstream en lecture. On peut aussi paramtrer cette ouverture :
ofstream out(name, ios::out|ios::nocreate);
fstream dictionary("dict.text", ios::in|ios::out);

10.5.2.

fermeture des flots

dictionary.close(); //pour fermer

A l'arrt du programme, les flots cin, cout et cerr sont automatiquement ferms

10.5.3.

flots de chanes de caractres

la classe strstream est dclare dans "strsream.h"


le caractre zro terminal est interprt comme la fin du fichier
char *p = new char[size];
ostrstream out(p,size);
out << 3 << "abcd";

10.5.4.

mise en tampon

Les entres sorties utilisent des mcanismes de tampons distincts suivant que le type de flot est un fichier, ou un
tableau de caractres. Le mcanisme de leur prise en compte tire profit de la possibilit d'avoir des fonctions de dbordement
(overflow et underflow) virtuelles.

10.6. entres/sorties C
On peut entremler des appels aux fonctions d'entre sortie C sur une base ligne ligne. (au niveau des caractres on
peut avoir des problmes de portabilit).
Certaines implantations de C++ demandent d'appeler une fonction membre static de la classe ios pour travailler avec
des fonctions C:
Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

111

Cours de programmation

Laurent Henocque

Le langage C++

ios::sync_with_stdio ();

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

112

Cours de programmation

Laurent Henocque

Le langage C++

11. Notes rapides au programmeur C


11.1. rgles empiriques de base pour la conception d'objets
si il y a structure : instance de classe
si visible comme objet : instance de classe
si deux classes partagent un concept : classe de base, et hritage
si la classe contient un objet d'un type donn : patron

11.2. notes aux programmeurs C


moins de prprocesseur : const, enum, inline, patrons
pas de malloc : new
moins d'unions : hritage
viter void *, +pointeur, tableaux C, cast explicite sauf au coeur des classes

Ecole Suprieure dIngnieurs de Luminy / dpartement ES2I, Marseille

113

Vous aimerez peut-être aussi