Cours P.O.O.
ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
CHAPITRE 2
DU C AU C++
Faten BEN HMIDA Université de La Manouba – ENSI – 2012/2013
Plan
2
Bref historique
Spécificités du C++ : synthèse
Commentaires
Types de données – Conversions
Déclarations et initialisations – Constantes
Entrées/Sorties
Fonctions – Paramètres – Surcharge – Fonctions inline
Références – passage de paramètres par référence
Gestion dynamique de la mémoire
Espaces de nommage
Faten Ben Hmida 1
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Bref historique
3
1980 : création du langage C++ par Bjarne
Stroustrup dans les laboratoires Bell d’AT&T.
1985 : parution de la première édition du livre « The
C++ programming Language »
1995-1997 : révision standard ANSI/ISO
Spécificités du C++ : synthèse
4
C++ présente, par rapport au C, des extensions :
qui permettent de supporter la POO.
qui ne sont pas orientées POO (contenu de ce chapitre) :
Nouvelle forme de commentaire.
Type booléen + définition implicite de nouveaux types de données.
Plus grande souplesse dans les déclarations et les initialisations.
Nouvelles possibilités d’entrées/sorties.
Surcharge des fonctions : attribution d’un même nom à différentes fonctions.
Notions de référence - passage d’arguments par référence.
Nouveaux opérateurs de gestion dynamique de la mémoire : new et delete.
Possibilité de définir des fonctions "en ligne" (inline).
Faten Ben Hmida 2
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Commentaires
5
Commentaires en C :
/* Commentaire en C pouvant s’étendre sur
plusieurs lignes */
Commentaires en C++ :
Commentaires de type C (pour désactiver une partie du code) :
/* Commentaire en C++ pouvant s’étendre sur
plusieurs lignes */
Nouveau type de commentaires (commentaire de fin de ligne) :
// Commentaire en C++ sur la même ligne
le commentaire de type // n'appartient qu'au C++
Commentaires
6
Nous pouvons mêler (volontairement ou non !) les deux types
de commentaires.
Dans l’exemple suivant :
/* partie1 // partie2 */ partie3
Le commentaire ouvert par /* ne se termine qu'au prochain */
donc partie1 et partie2 sont des commentaires, tandis que
partie3 est considérée comme appartenant aux instructions.
De même, dans :
partie1 // partie2 /* partie3 */ partie4
Le commentaire introduit par // s’étend jusqu’à la fin de la
ligne. Il concerne donc partie2, partie3 et partie 4.
Faten Ben Hmida 3
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Types de données
7
Le langage C ne possède pas de type booléen.
C++ a introduit un nouveau type bool pour pallier cette
carence.
Le type bool est formé de deux valeurs notées true et false.
bool flag = true;
...
do
{
...
if ( ... )
flag = false;
...
} while (flag == true);
Types de données
8
En théorie, les résultats des comparaisons ou des combinaisons
logiques doivent être de type bool. Toutefois, il existe des
conversions implicites :
bool type numérique : true devient 1, false devient 0.
type numérique bool : toute valeur ≠ 0 devient true et 0
devient false.
Dans ces conditions, tout se passe finalement comme si bool
était un type énuméré défini ainsi :
typedef enum { false=0, true } bool;
Faten Ben Hmida 4
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Types de données
9
Nouvelle possibilité en C++ : Définition implicite de types sans avoir
à utiliser le mot clé typedef
struct coordonnees {
int x;
int y;
};
enum couleur = {rouge, vert, bleu};
union numerique {
int entier;
double reel;
};
coordonnees, couleur et numerique sont considérés comme
types de données et peuvent être utilisés en tant que tels.
Conversions de types
10
C et C++ autorisent le mélange de données de différents
types dans une même expression.
Nécessité de convertir toutes les données dans un type commun
avant d’évaluer l’expression.
2 types de conversions :
Conversions implicites (automatique) : mises en place
automatiquement par le compilateur en fonction du contexte sans
l’intervention du programmeur.
Conversions explicites (cast) : mises en place par le programmeur qui
choisit explicitement le type dans lequel les données doivent être
converties.
Faten Ben Hmida 5
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Conversions implicites
11
Le compilateur tente généralement de convertir les données du type le
plus ‘petit’ vers le type le plus ‘large’ conversion non dégradante
float X = 12.3;
int N = 5;
X = X + N; //la valeur de N est convertie en float (5.0)
Dans le cas d’une affectation, le résultat de l’évaluation de
l’expression à droite, est converti dans le type de la variable à
gauche. Il peut y avoir perte de précision si le type de la destination
est plus faible que celui de la source conversion dégradante
float X = 12.3;
int N;
N = X; //la valeur de X est convertie en int (12)
Conversions implicites
12
Exemple :
int X;
float A = 12.48;
char B = 4;
X = A / B;
B est converti en float (conversion non dégradante), le résultat
de la division est de type float (valeur 3.12) et sera converti
en int avant d'être affecté à X (conversion dégradante), ce qui
conduit au résultat X=3.
Faten Ben Hmida 6
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Conversions explicites
13
Le programmeur peut recourir à une conversion explicite (ou
cast) pour forcer la conversion dans un type particulier.
2 manières d’effectuer une conversion explicite en C++ :
Ancienne notation introduite par C
(type) expression
Nouvelle notation fonctionnelle introduite par C++
type (expression)
Possibilité de mixage des deux notations
(type) (expression)
Conversions explicites
14
Exemples :
int A = 3, B = 4;
float C;
C = (float) A / B;
La valeur de A est explicitement convertie en float. La valeur
de B est automatiquement convertie en float. Le résultat de la
division (type rationnel, valeur 0.75) est affecté à C. Résultat:
C=0.75
double d = 2.5;
int i;
i = int(d); // i = 2 (conversion dégradante)
Faten Ben Hmida 7
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Exercice 1
15
Soient les déclarations :
char c = 'A' ;
int n = 5 ;
long p = 1000 ;
float x = 1.25 ;
double z = 5.5 ;
Quels sont le type et la valeur de chacune des expressions suivantes :
n + c + p /* 1 */
2 * x + c /* 2 */
(char) n + c /* 3 */
(float) z + n / 2 /* 4 */
Exercice 2
16
Soient les déclarations :
int n = 5, p = 9, int q ;
float x ;
Quels sont le type et la valeur de chacune des expressions suivantes :
q = n < p ; /* 1 */
q = n == p ; /* 2 */
q = p % n + p > n ; /* 3 */
x = p / n ; /* 4 */
x = (float) p / n ; /* 5 */
x = (p + 0.5) / n ; /* 6 */
x = (int) (p + 0.5) / n ; /* 7 */
q = n * (p > n ? n : p) ; /* 8 */
q = n * (p < n ? n : p) ; /* 9 */
Faten Ben Hmida 8
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Déclarations et initialisations
17
C++ offre une plus grande souplesse en matière de
déclarations que le C.
Il n'est plus obligatoire de regrouper les déclarations au début
d’un bloc ou d'une fonction comme en C.
En C++, il est possible de déclarer des variables à n’importe
quel endroit du code, à condition que ce soit fait avant leur
utilisation.
La portée de la variable reste limitée au bloc ou à la fonction
dans laquelle elle a été déclarée.
Déclarations et initialisations
18
C Séparation entre les
déclarations qui se
int a=5, b=2, n;
float x; trouvent au début du
... code et les instructions qui
n = a / b; viennent après.
x = a / (float) b;
...
C++
int a=5, b=2, n;
...
n = a / b;
Déclaration tardive d’une ...
variable au milieu des float x;
instructions : plus grande x = a / (float) b;
liberté d’emplacement ...
des déclarations.
Faten Ben Hmida 9
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Déclarations et initialisations
19
L’instruction suivante est acceptée en C++ mais pas en C :
for (int i=0 ; i<N ; i++)
{
x += i; // i variable locale de parcours
}
Cette possibilité s'applique à toutes les instructions structurées,
c’est-à-dire aux instructions for, switch, while et do…while.
Dans ce cas la portée de la variable i est limitée au bloc régi
par la boucle for : en dehors de ce bloc elle ne sera pas
reconnue !
Déclarations et initialisations
20
En C++ les expressions utilisées pour initialiser une variable peuvent
être quelconques, alors qu'en C elles ne peuvent faire intervenir que
des variables dont la valeur est connue dès l'entrée dans la fonction
concernée.
Voici un exemple incorrect en C et accepté en C++ :
main()
{
int n ;
...
n = ...
...
int q = 2*n - 1 ;
...
}
Faten Ben Hmida 10
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Constantes
21
En C il existe deux types de constantes :
Constantes symboliques : constantes typées définies grâce au
mot clé const. La valeur d’une constante symbolique n’est pas
connue au moment de la compilation.
const int n = 10;
int tab[n]; /* interdit en C */
Constantes nommées : constantes non typées définies grâce à la
directive du préprocesseur #define. La valeur d’une constante
nommée est connu au moment de la compilation.
#define N 10
...
int tab[N];
char ch[2*N];
/* autorisé car la valeur de N est connue à ce niveau */
Constantes
22
En C++ le mot clé const permet de définir des constantes
typées dont la valeur est connue au moment de la compilation
(permet de remplacer la directive du préproceseur #define)
const int n = 10;
int tab[n]; // interdit en C et autorisé en C++
char ch[2*n]; // interdit en C et autorisé en C++
Exemples
const float PI = 3.14;
const int MAX = 100;
const char NOM[] = "etudiant II1";
Faten Ben Hmida 11
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Constantes
23
Utilisation de const avec des pointeurs
int n=8, m=5, p=3;
const int * N = &n; //pointeur sur entier constant
int * const M = &m; //pointeur constant sur entier
const int * const P = &p;
//pointeur constant sur entier constant
De manière générale
const T * p = &v;
// pointeur modifiable p vers un objet non modifiable de type T
T * const p = &v;
// pointeur p non modifiable vers un objet modifiable de type T
const T * const p = &v;
// pointeur p non modifiable vers un objet non modifiable de
// type T
Constantes
24
Exemples
Donnée pointée constante
const char * ptr = "hello";
*ptr = ‘H’; // Erreur (assignement to read-only location)
ptr++; // Correct
Pointeur constant
char * const ptr = "hello";
*ptr = ‘H’; // Correct
ptr++; // Erreur (increment of read-only variable)
Donnée pointée et pointeur constants
const char * const ptr = "hello";
*ptr = ‘H’; // Erreur (assignement to read-only location)
ptr++; // Erreur (increment of read-only variable)
Faten Ben Hmida 12
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Entrées / Sorties
25
En C++, les fonctions d’entrée/sortie standard en C telles que
printf() et scanf() déclarées dans le fichier d’en-tête
<stdio.h> restent utilisables.
Nouvelles possibilités d’entrées/sorties en C++ reposant sur la
notion de flot.
Ces nouvelles possibilités ne nécessitant pas le FORMATAGE des
données !
La bibliothèque standard <iostream.h> du langage C++ fournit
des opérations qui peuvent se substituer à celles de la
bibliothèque <stdio.h> du langage C.
Entrées / Sorties
26
Flots standards
C C++
Entrées stdin cin
Sorties stdout cout
Erreurs stderr cerr
La sortie de données sur les flots de sortie (cout et cerr) se
fait en utilisant l’opérateur de redirection <<
L’entrée de données à partir du flot d’entrée (cin) se fait en
utilisant l’opérateur de redirection >>
Faten Ben Hmida 13
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Entrées / Sorties
27
Ecrire des données avec cout
#include<iostream.h>
...
int n = 25;
cout << "valeur de n :" ;
cout << n;
Ces 2 dernières instructions permettent d’afficher le résultat suivant :
valeur de n : 25
Elles peuvent être remplacées par l’instruction suivante :
cout << "valeur de n :" << n;
Entrées / Sorties
28
Cout permet d’écrire des données de tout type sans formatage
préalable :
int n = 25; char c = 'a'; float x = 12.345;
char * ch = "bonjour"; int * ad = &n;
cout << "valeur de n : " << n << '\n';
cout << "valeur de n^2 : " << n*n << '\n';
cout << "caractere c : " << c << "\n";
cout << "valeur de x : " << x << "\n";
cout << "chaine ch : " << ch << endl;
cout << "adresse de n : " << ad << endl;
Pour le retour à la ligne nous pouvons utiliser soit le caractère
'\n', soit la chaîne "\n" ou simplement l’objet endl.
Faten Ben Hmida 14
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Entrées / Sorties
29
Lire des données avec cin
#include<iostream.h>
...
int id; char nom[20]; float moyenne;
cout << "saisir l’identifiant, le nom et la moyenne" ;
cin >> id;
cin >> nom;
cin >> moyenne;
Ces 3 dernières instructions peuvent être remplacées par l’instruction
suivante :
cin >> id >> nom >> moyenne;
Fonctions
30
Mêmes règles de définition qu’en C
type_ret nom_fnct(type_1 arg_1, .., type_n arg_n)
{
// déclarations locales
// instructions
}
Typage obligatoire des fonctions : toute fonction doit avoir un
type de retour, si elle ne retourne rien le type doit
obligatoirement être void.
Déclaration obligatoire des fonctions grâce aux prototypes si
nécessaire.
Faten Ben Hmida 15
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Appel de fonction et conversion de type
31
Le C++, contrairement au C, autorise dans une certaine mesure
le non-respect du type des arguments lors d’un appel de
fonction : le compilateur effectue alors une conversion de type.
double ma_fonction(int u, float f);
...
int main()
{
char c; int i, j; float x; double r1, r2, r3, r4;
r1 = ma_fonction(i,x); // appel standard
r2 = ma_fonction(c,x); // correct, c est converti en int
r3 = ma_fonction(i,j); // correct, j est converti en float
r4 = ma_fonction(x,j); // correct, x est converti en int
// et j est converti en float
}
Surdéfinition/surcharge des fonctions
32
Surdéfinition/surcharge d’une fonction : possibilité d’attribuer le même
nom à plusieurs fonctions pouvant effectuer des traitements différents
(avec des conditions). Ceci est interdit en C, mais autorisé en C++.
Lors de l’appel, le compilateur identifie la bonne fonction grâce à sa
signature (nombre et types des arguments). Le type de retour ne fait
pas partie de la signature, il n’est donc pas pris en compte dans
l’identification de la fonction.
Exemple :
int fonction(int n) { ... }
int fonction(float x) { ... }
void fonction(int n, int p) { ... }
float fonction(float x, int n) { ... }
void fonction(float x, int n) { ... } // erreur : même signature
Faten Ben Hmida 16
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Règles de recherche d’une fonction surchargée
33
Le compilateur recherche la "meilleure correspondance" possible.
Il y a plusieurs niveaux de correspondance :
1) Correspondance exacte : le type de l’argument passé correspond
exactement au type du paramètre formel.
2) Correspondance avec promotion numérique : c.à.d. avec un recours à une
conversion de types, essentiellement char et short int et float double
3) Conversions standards : il s'agit des conversions légales en C++ ; en
général toute conversion d'un type numérique en un autre type numérique.
Si plusieurs fonctions conviennent, il y a erreur de compilation
due à l'ambiguïté. De même, si aucune fonction ne convient à
aucun niveau, il y a aussi erreur de compilation.
Exercice
34
Soient les déclarations suivantes :
int fct (int) ; // fonction I
int fct (float) ; // fonction II
void fct (int, float) ; // fonction III
void fct (float, int) ; // fonction IV
int n, p ; float x, y ; char c ; double z ;
Les appels suivants sont-ils corrects et, si oui, quelles seront les fonctions
effectivement appelées et les conversions éventuellement mises en place ?
a. fct (n) ; f. fct (n, p) ;
b. fct (x) ; g. fct (n, c) ;
c. fct (n, x) ; h. fct (n, z) ;
d. fct (x, n) ; i. fct (z, z) ;
e. fct (c) ;
Faten Ben Hmida 17
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Paramètres avec valeurs par défaut
35
Il arrive parfois qu’on soit amené à passer toujours la même valeur en
paramètre à une fonction.
C++ offre la possibilité de définir des valeurs par défaut pour les
paramètres d’une fonction lors de sa déclaration (dans le prototype).
Restriction importante : les paramètres ayant une valeur par défaut
doivent être les derniers de la liste des paramètres c.à.d. déclarés le
plus à droite respect de la contigüité.
Exemple :
int fonction_1(int n, int p=5);
void fonction_2(float x, int n, char c='A', int p=2);
Paramètres avec valeurs par défaut
36
Exemple :
void f(int n, int p=1, float x=2.5);
...
int main()
{
...
f(a); // correct ~ f(a, 1, 2.5)
f(a, b); // correct ~ f(a, b, 2.5)
f(a, b, c); // correct : présence des 3 paramètres
...
}
Faten Ben Hmida 18
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Paramètres avec valeurs par défaut
37
Pourquoi faut-il respecter la contigüité ?
void f(int p=1, int n, float x=2.5);
...
int main()
{
...
f(a, b, c); // présence des 3 paramètres
f(a); // f(1, a, 2.5)
f(a, b); // problème !
// f(a, b, 2.5) ou f(1, a, b) ?
...
}
Paramètres anonymes
38
En C le nommage des paramètres d’une fonction est obligatoire.
int main(int argc, char* argv[])
{
...
}
En C++, il est possible de déclarer des paramètres anonymes
dans la signature d’une fonction.
Paramètre anonyme : paramètre dont seul le type est spécifié.
int main(int, char**)
{
//paramètres anonymes non utilisés
}
Faten Ben Hmida 19
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Références/Alias
39
En C, pour référencer une variable nous utilisions des pointeurs :
int n ;
int* p = &n ; // p est une référence à n
En C++, une référence (ou alias) est un identificateur
supplémentaire pour une variable (deuxième nom).
Pour créer une référence en C++, on utilise l’opérateur de
référence & :
int n ;
int& r = n ; // r est une référence à n
Références/Alias
40
Physiquement, une référence est un pointeur constant sur la
variable référencée, mais qui est manipulée par le programme
comme s'il s'agissait de la variable elle-même.
int n ;
int& r = n ; // r et n désignent la même variable
n = 3 ;
cout << r ; // affiche 3
r = 5 ;
cout << n ; // affiche 5
En C++, toute opération citant une référence porte sur la
variable référencée et non sur le pointeur.
Faten Ben Hmida 20
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Références/Alias
41
Une référence ne peut se référer qu’à une seule variable
pendant sa durée de vie :
int n, p=9 ;
int& r = n ;
r = p ; /* signifie que la valeur de r et donc celle
de n reçoit la valeur de p et ne signifie pas que r
devient une référence sur p ! */
Une référence doit obligatoirement être initialisée au moment
de sa déclaration, elle ne peut pas être initialisée avec une
valeur constante ni avec une expression :
int& r ; // incorrect
int& r = 5 ; // incorrect
int& r = 2*n ; // incorrect
Références/Alias
42
Une référence ne peut se référer qu’à une seule variable
pendant sa durée de vie :
int i=5, j=6;
int& k = i; // k variable référence sur i
cout << i << " " << j << " " << k << endl; // affiche 5 6 5
k=3;
cout << i << " " << j << " " << k << endl; // affiche 3 6 3
i=8;
cout << i << " " << j << " " << k << endl; // affiche 8 6 8
k=j;
cout << i << " " << j << " " << k << endl; // affiche 6 6 6
k=1;
cout << i << " " << j << " " << k << endl; // affiche 1 6 1
Faten Ben Hmida 21
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Passage de paramètres
43
En C, le passage de paramètres à une fonction se fait uniquement
par valeurs.
Le passage par valeurs ne permet pas de modifier les
paramètres effectifs.
Pour simuler un passage par adresses on est obligé de travailler
avec les pointeurs (le passage se fait toujours par valeurs mais
dans ce cas il s’agit de la valeur d’un pointeur c.à.d. une adresse)
C++ pallie cette lacune en introduisant le passage de
paramètres par références
Passage par valeurs (rappel)
44
Faten Ben Hmida 22
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Passage par adresses (rappel)
45
Passage par références
46
void increment (int& n, int i)
{ n = n + i ; }
...
int main()
{ int x = 7 ;
increment(x, 3) ;
cout << x << endl ; // affiche 10
}
La notation int& n signifie que le paramètre n de type int est passé
par référence.
Le compilateur se charge d’extraire l’adresse du paramètre : le
passage se fait par adresse mais de manière implicite.
Dans le corps de la fonction on manipule le paramètre n lui même et
non un pointeur sur ce paramètre. De même au moment de l’appel on
passe une valeur (variable) et non une adresse (pointeur).
Faten Ben Hmida 23
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Passage par références
47
Passage par références
48
Version C Version C++
void permut (int* a, int* b) void permut (int& a, int& b)
{ {
int tmp = *a; int tmp = a;
*a = *b; a = b;
*b = tmp; b = tmp;
} }
... ...
int i=2, j=4; int i=2, j=4;
permut(&i, &j); permut(i, j);
/* i vaut 4 et j vaut 2 */ /* i vaut 4 et j vaut 2 */
Faten Ben Hmida 24
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Propriétés du passage par référence
49
Absence de conversion
Dès lors qu’une fonction prévoit un passage de paramètres par
référence, les conversions de type à l’appel ne sont plus
autorisées : le type du paramètre effectif doit correspondre
exactement au type du paramètre formel.
void f(int& n) ; // f reçoit un entier par référence
float x ;
...
f(x) ; // incorrect : discordance de types
Exception à cette règle : cas des paramètres formels constants !
Propriétés du passage par référence
50
Cas d’un paramètre effectif constant
Dès lors qu’une fonction prévoit un passage de paramètres par
référence, elle n’admettra pas de constantes comme paramètres
effectifs au moment de son appel.
void f(int& n) ; // f reçoit un argument par référence
...
f(3) ; // incorrect : f ne peut pas modifier une constante
const int c = 5;
f(c) ; // incorrect : f ne peut pas modifier une constante
Exception à cette règle : cas des paramètres formels constants !
Faten Ben Hmida 25
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Propriétés du passage par référence
51
Cas d’un paramètre formel constant
Considérons une fonction ayant le prototype suivant :
void f(const int& n) ;
Les appels suivants sont corrects :
const int c = 5 ;
float x;
...
f(3) ; // correct
f(c) ; // correct
f(x) ; // correct
Dans le dernier cas, f reçoit la référence à une variable temporaire
contenant le résultat de la conversion de x en int
Retour d’une référence
52
Le passage des arguments par référence s'applique aussi à la valeur
de retour d'une fonction.
Il est possible qu’une fonction en C++ retourne une référence.
int& f ()
{ ...
return n ;
}
Un appel de f provoquera la transmission en retour non plus d’une
valeur, mais de la référence de n . Cependant, on peut utiliser f d’une
façon usuelle :
int p ;
...
p = f() ; // affecte à p la valeur située à la référence
// fournie par f
Faten Ben Hmida 26
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Fonctions en ligne (inline)
53
Rappel sur les macros en C
Macro : notion voisine d’une fonction (petite fonction)
Une macro et une fonction s'utilisent apparemment de la même façon,
en faisant suivre leur nom d'une liste d’arguments entre parenthèses.
Elle permet de factoriser un morceau de code peu volumineux sans
avoir recours au x fonctions
Une macro est définie en utilisant la directive #define du préprocesseur
#define MAX(a,b) (((a)>=(b))?(a):(b))
Une macro est incorporée dans le code par le préprocesseur à chaque
fois qu’elle est appelée (remplacement textuel),
Fonctions en ligne (inline)
54
Rappel sur les macros en C
L’utilisation des macros engendre du code volumineux et donc une perte
d’espace mémoire d’autant plus importante que le code correspondant
à la macro est important.
L’utilisation des macros permet de gagner en temps d’exécution puisqu’il
n’y a pas de mécanisme d’appel comme dans les fonctions.
Les macros présentent un risque conséquent d’effet de bord lié à leur
mauvaise définition et/ou utilisation :
#define CARRE(a) ((a)*(a))
CARRE(n++) sera remplacée par (n++)*(n++) et donc n sera
incrémentée 2 fois !
Faten Ben Hmida 27
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Fonctions en ligne (inline)
55
Macros Fonctions
Avantages : Avantages :
Gain en temps d’exécution Gain en espace mémoire
Sécurité grâce au contrôle du type
Inconvénients : des données
Perte d’espace mémoire Pas de risques d’effet de bord
Pas de contrôle sur le type des
données Inconvénients :
Risques d’effet de bord Perte de temps d’exécution dû au
indésirables / inattendus mécanisme d’appel
Fonctions en ligne (inline)
56
C++ nous offre la possibilité de tirer profit des avantages des macros
et de ceux des fonctions en définissant les fonctions en ligne (ou inline)
Une fonction en ligne se définit et s'utilise comme une fonction ordinaire,
à la seule différence qu'on fait précéder son en-tête du mot clé inline
inline int max(int a, int b) { return (a>=b)?a:b); }
Lorsqu’une fonction est déclarée inline, le compilateur la traite comme
s’il s’agissait d’une macro (remplacement de code) dans la mesure du
possible (fonction peu volumineuse).
Les fonctions inline permettent d’avoir la sécurité d’une fonction
ordinaire (pas d’effet de bord) et la rapidité d’une macro.
Faten Ben Hmida 28
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Gestion dynamique de la mémoire
57
En C, l’allocation et libération de la mémoire se font grâce aux
fonctions malloc() et free(). Ces fonctions restent valables
en C++.
C++ a introduit de nouveaux opérateurs pour l’allocation et la
libération de la mémoire : new et delete.
Gestion dynamique de la mémoire
58
L’opérateur new s'utilise ainsi pour créer des variables simples :
new type
où type représente un type quelconque. Il fournit comme résultat
un pointeur (de type type*) sur l'emplacement mémoire
correspondant.
L’opérateur new s’utilise aussi pour créer des tableaux :
new type[n]
où n désigne une expression entière quelconque (non négative).
Cette instruction alloue l'emplacement nécessaire pour n éléments
du type indiqué ; elle fournit en résultat un pointeur (de type
type*) sur le premier élément de ce tableau.
Faten Ben Hmida 29
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Gestion dynamique de la mémoire
59
Lorsqu'on souhaite libérer un emplacement préalablement alloué
par new, on doit utiliser l'opérateur delete.
delete ptr;
Cette instruction permet de libérer l’emplacement alloué à une
variable simple et dont l’adresse se trouve dans le pointeur ptr.
Lorsqu'on souhaite libérer un emplacement préalablement alloué
par new ..[], on doit utiliser l'opérateur delete[].
delete[] tab;
Cette instruction permet de libérer l’emplacement alloué à un
tableau et dont l’adresse se trouve dans le pointeur tab.
Gestion dynamique de la mémoire
60
malloc() / free() new / delete
//Allocation d’une variable simple //Allocation d’une variable simple
int* ptr; int* ptr;
ptr = (int*) malloc(sizeof(int)); ptr = new int;
... ...
//Libération d’une variable simple //Libération d’une variable simple
free(ptr); delete ptr;
//Allocation d’un tableau //Allocation d’un tableau
int n = N_MAX; int n = N_MAX;
int* tab; int* tab;
tab = (int*) malloc(n*sizeof(int)); tab = new int[n];
... ...
//Libération d’un tableau //Libération d’un tableau
free(tab); delete[] tab;
Faten Ben Hmida 30
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Structure d’un programme C++
61
La structure d'un programme C++ est très similaire à la structure
d'un programme C. On y retrouve :
une fonction principale : main() c’est le point d’entrée au
programme.
Les fichiers d'en-tête (extension .h) regroupent les déclarations de
types et de classes, les prototypes des fonctions…
Les fichiers sources (extensions : .c, .cpp) comportent le code
proprement dit : le corps des fonctions et des méthodes, la
déclaration de variables, d'objets.
Inclusions de bibliothèques
62
La manière classique d'inclure les fichiers d'en-tête associés aux
bibliothèques en C était la suivante :
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
En C++, il n'est plus nécessaire de citer l'extension .h pour les
fichiers d'en-tête standards, mais il faut tout de même spécifier s’il
s’agit d’un fichier correspondant à une librairie C, et dans ce cas
on ajoute le caractère 'c' devant le nom du fichier :
#include <cstdio>
#include <cstdlib>
#include <cmath>
Faten Ben Hmida 31
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Inclusions de bibliothèques
63
Ancienne version en C++ (avant la normalisation)
#include <iostream.h> // obsolète (deprecated)
...
cout << "message" ;
Nouvelles versions (après la normalisation)
#include <iostream>
...
std::cout << "message" ;
ou bien (utilisation de l’espace de noms std) :
#include <iostream>
using namespace std;
...
cout << ...
Espaces de noms
64
Problème : un programme peut utiliser différentes bibliothèques
dans lesquelles il se peut que les mêmes noms identifient des
composants différents.
// achats.h
struct Commande { ... }; // à un fournisseur Comment éviter les conflits
struct Facture { ... }; // d'un fournisseur de noms ?
// ventes.h
struct Commande { ... }; // d'un client En C, on était obligé de
struct Facture { ... }; // à un client
choisir des noms différents !
// stocks.h
struct Article { ... };
En C++, le problème est
// gestion.cpp résolu grâce à l’utilisation
#include "achats.h"
#include "ventes.h" des espaces de noms
#include "stock.h"
int main ()
{ Commande cmd; // Commande client ou Commande fournisseur ???
Faten Ben Hmida 32
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Espaces de noms
65
Nouveau concept introduit par C++. Il s'agit de donner un nom à
un regroupement de déclarations (types, fonctions, constantes …),
en procédant ainsi :
namespace esp_nom
{
// déclarations
}
On peut lever l'ambiguïté lorsqu'on utilise plusieurs espaces de
noms comportant des identificateurs identiques ; pour cela il suffit
de faire appel à l'opérateur de résolution de portée ::
esp_nom::ident ...
// on se réfère à l'identificateur ident de l'espace de
// noms esp_nom
Espaces de noms
66
Pour se référer à des identificateurs définis dans un espace de
noms sans recourir à l’opérateur de résolution de portée, on utilise
l’instruction using :
using namespace esp_nom;
dorénavant, l'identificateur ident employé seul (sans esp_nom::)
correspondra à celui défini dans l'espace de noms esp_nom.
Remarque : Tous les identificateurs des fichiers d’en-tête standard
sont définis dans l’espace de noms std ; il est donc nécessaire de
recourir systématiquement à l’instruction :
using namespace std ;
Faten Ben Hmida 33
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Inclusions de bibliothèques
67
Ancienne version en C++ (avant la normalisation)
#include <iostream.h> // obsolète (deprecated)
...
cout << "message" ;
Nouvelles versions (après la normalisation)
#include <iostream>
...
std::cout << "message" ;
ou bien (utilisation de l’espace de noms std) :
#include <iostream>
using namespace std;
...
cout << ...
Espaces de noms
68
Exemple
namespace Achats void main ()
{ #include "achats.h" {
// autres déclarations ... /* 1) désignation explicite de
} composants appartenant à des
espaces différents */
namespace Ventes Achats::Commande cde_fourn;
{ #include "ventes.h" Ventes::Commande cde_client;
// autres déclarations ...
} /* 2)importation locale d'un
identifiant de composant : */
using Ventes::Facture;
namespace Stocks
{ #include "stocks.h" Facture fact_client;
// autres déclarations ...
} /* 3) mise à disposition de tous les
identifiants d'un espace de noms */
using namespace Stocks;
Article prod;
Faten Ben Hmida 34
Cours P.O.O. ENSI - II1
Chapitre 2 : du C au C++ A.U. : 2012/2013
Espaces de noms
69
Exemple
namespace versionC void main ()
{ void permut(int* a, int* b) {
{ int tmp = *a; int i=5, j=2;
*a = *b;
versionC::permut(&i,&j);
*b = tmp;
} cout << "i=" << i << " j=" << j;
} cout << endl;
namespace versionCPP versionCPP::permut(i,j);
{ void permut(int& a, int& b) cout << "i=" << i << " j=" << j;
{ int tmp = a;
a = b; // ou bien
b = tmp; // using namespace versionCPP;
} // permut(i,j);
}
Faten Ben Hmida 35