COURS PROGRAMMATION C++ I DIGL L2 2I
Chapitre 4 : Les tableaux et fonctions en C++
I. Les tableaux
1. Les tableaux à une dimension
Les tableaux sont des structures de données constituées d'un certain nombre d'éléments de même type. On peut accéder directement
à un élément du tableau en indiquant son indice entre crochets (indice de 0 à nombre_d_éléments-1).
Les tableaux statiques
Syntaxe
type identificateur[taille];
Sémantique
identificateur est un tableau de taille éléments de type type. taille est obligatoirement une valeur constante. La taille du
tableau est donc figée une fois pour toute et ne peut pas être modifiée en cours d'exécution.
Pour désigner la i-ième case de tableau, on écrit
identificateur[i]
où i est un entier quelconque. Les cases sont numérotées à partir de 0 : les valeurs possibles de i vont donc de 0 à taille-1.
Pointeurs et tableaux
Il existe un lien entre les pointeurs et les tableaux : identificateur est en fait un pointeur constant vers un élément de type type,
pointant le premier élément du tableau, en d'autres termes le nom d'un tableau est un pointeur constant sur le premier élément du
tableau.
Exemple
#include <iostream>
using namespace
std; int main()
{
int i;
int const tailleTableau(10); //taille du tableau
int tableau[tailleTableau]; //déclaration du tableau
for (int i(0); i<tailleTableau; i++ )
{
tableau[i]=i*i;
cout<<"Le tableau ["<<i<<"] contient la valeur "<<tableau[i]<<endl;
}
return 0;
}
1 Adner Sodrel LEPHOYO
Exécution
COURS PROGRAMMATION C++ I DIGL L2 2I
Le tableau [0] contient la valeur 0
Le tableau [1] contient la valeur 1
Le tableau [2] contient la valeur 4
Le tableau [3] contient la valeur 9
Le tableau [4] contient la valeur 16
Le tableau [5] contient la valeur 25
Le tableau [6] contient la valeur 36
Le tableau [7] contient la valeur 49
Le tableau [8] contient la valeur 64
Le tableau [9] contient la valeur 81
Les tableaux dynamiques
Un tableau dynamique est un tableau dont le nombre de cases peut varier au cours de l'exécution du programme. Il permet
d'ajuster la taille du tableau au besoin du programmeur.
L'opérateur new
Syntaxe :
pointeur=new type[taille];
L'opérateur new[] permet d'allouer une zone mémoire pouvant stocker taille éléments de type type, et
retourne l'adresse de cette zone. Le paramètre taille est un entier qui peut être quelconque (variable, constante,
expression). new renverra un pointeur vers un type. La variable pointeur est donc du type type *. Les cases
du tableau seront numérotées de 0 à taille-1 et on y accédera comme un tableau statique. S'il n'y a pas assez de
mémoire disponible, new renvoie le pointeur NULL.
L'opérateur delete[]
Syntaxe :
delete[] pointeur;
Cette utilisation de delete[] permet de détruire un tableau précédemment alloué grâce à new[].
N'oubliez surtout pas les crochets juste après delete, sinon le tableau ne sera pas correctement libéré.
Le programmeur en C++ doit gérer la destruction effective des tableaux qu'il a créé dynamiquement.
Exemple
2 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I
#include <iostream>
using namespace std;
int main()
{
int i, taille;
cout << "Tapez la valeur de taille : "; cin >> taille;
int *t;
t = new int[taille];
for (i = 0; i < taille; i++) t[i] = i * i;
for (i = 0; i < taille; i++) cout << t[i] << endl;
delete[] t;
return 0;
}
Exécution 1
Tapez la valeur de taille : 4
0
Exécution 2
Tapez la valeur de taille : 6
0
1
4
9
16
25
Tableaux multidimensionnels
Un tableau peut avoir plus d'une dimension (matrice 2D, matrice 3D, ...). Par contre, on ne peut pas utiliser l'expression
suivante pour l'allocation dynamique de tableaux à plusieurs dimensions :
pointeur=new type[taille1][taille2]...;
L'allocation dynamique de tableaux à deux dimensions est expliquée ci-dessous.
Les vectors
Les vectors (vecteur en français) sont un type de tableaux dynamiques très puissants qui suivent la syntaxe suivante :
vector<type> nom(taille);
Par exemple, un tableau dynamique constitué de 5 entiers et nommé « tableau_nombre_entiers » sera défini de la sorte :
3 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I
vector<int> tableau_nombres_entiers(5);
Mais on peut bien sûr définir un tableau qui ne comporte aucune taille, ce qui est bien utile pour un tableau... dynamique !
vector<int> tableau_nombres;
Pour accéder aux valeurs d'un vector, on procède la même manière que pour tous les tableaux, par exemple :
tableau_nombres_entiers[0] = 0;
tableau_nombres_entiers[1] = 2;
...
Mais on peut également affecter à tout le vector une même valeur en l'indiquant juste après la taille avec une virgule.
Exemple :
#include <iostream>
#include <string> //ATTENTION : PENSER À INCLURE "STRING"
using namespace std;
vector<int> tableau_nombre_entiers(5, 10); //Ce tableau est composé de 5 nombres entiers qui
sont tous égaux à 10
vector<string> tableau_lettres(4, "bonjour !"); //Ce tableau est composé de 4 chaînes de
caratères qui sont égales à
« bonjour ! »
Cela signifie que
tableau_nombre_entiers[0]=tableau_nombre_entiers[1]=tableau_nombre_entiers[2]=tableau_nombre_entiers[3]=tablea
u_nombre_entiers[4]=10 et de même
tableau_lettres[0]=tableau_lettres[1]=tableau_lettres[2]=tableau_lettres[3]="bonjour !".
Comme on le sait, les vectors sont des tableaux dynamiques, ainsi peuvent-ils s'agrandir ou se rétrécir. Pour ajouter une
case supplémentaire au tableau, il suffit de faire appel à la fonction push_back() :
vector<int> tableau_entiers;
tableau_entiers.push_back(7); //On ajoute une première case au tableau qui comporte le
nombre 7
tableau_entiers.push_back(18); //On ajoute une deuxième case au tableau qui comporte le
nombre 18
...
À l'inverse, pour supprimer une case, on utilise la fonction pop_back() :
vector<int> tableau_entiers(10); //Un tableau dynamique d'entiers à 10 cases
tableau_entiers.pop_back(); //Plus que 9 cases
tableau_entiers.pop_back(); //Plus que 8 cases
...
Enfin, pour récupérer la taille du vector, on utilise la fonction size() :
vector<int> tableau_entiers(5); //Un tableau de 5 entiers
int taille = tableau_entiers.size(); //'taille' a pour valeur 5
4 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I
Parcours d'un tableau
Le parcours des éléments d'un tableau se fait généralement avec une boucle for (on peut aussi utiliser while ou
do...while). Exemple :
int nombres[10] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };
for (int i = 0; i < 10; i++) // i va de 0 à 9 inclus
cout << "nombres [ " << i << " ] = " << nombres[i] << endl;
Les éléments d'un tableau sont stockés dans des zones contigües. Il est donc possible d'utiliser un pointeur pour parcourir
un tableau :
int nombres[10] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };
int *p = nombres;
for (int i = 0; i < 10; i++, p++)
cout << "Nombre : " << *p << endl;
Initialement le pointeur p pointe le premier élément du tableau. L'instruction p++ dans la boucle for incrémente le
pointeur, c'est-à-dire qu'il passe à l'élément suivant. L'adresse contenue dans le pointeur n'augmente pas de 1, mais de la
taille de l'élément pointé (ici int, soit 4 octets en général).
2. Les tableaux à deux dimensions
Statiques
Syntaxe
type identificateur[taille_i][taille_j];
Sémantique :
identificateur est un tableau de taille_i sur taille_j cases. Pour chaque case i choisie, il y a taille_j cases disponibles. Les taille_i et taille_j ne
peuvent changer au cours du programme. Pour le faire, il faut utiliser les tableaux dynamiques.
Exemple
#include <iostream>
using namespace std;
int main() {
int t[10][5];
for (int i = 0; i < 10; i++)
for (int j = 0; j < 5; j++)
t[i][j] = i * j;
for (int i = 0; i < 10; ++i)
{ for (int j = 0; j < 5;
++j){ cout << t[i][j] <<
" ";
}
cout << endl;
}
return 0;
}
5 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I Exécution
0 0 0 0 0
0 1 2 3 4
0 2 4 6 8
0 4 8 12 16
0 5 10 15 20
0 6 12 18 24
0 7 14 21 28
0 8 16 24 32
Dynamiques
Exemple
#include <iostream>
using std::cout;
using std::cin;
int **t;
int nColonnes;
int nLignes;
void Free_Tab(); // Libérer l'espace mémoire;
int main(){
cout << "Nombre de colonnes : "; cin >> nColonnes;
cout << "Nombre de lignes : "; cin >> nLignes;
/* Allocation dynamique */
t = new int* [ nLignes ];
for (int i=0; i < nLignes; i++)
t[i] = new int[ nColonnes ];
/* Initialisation */
for (int i=0; i < nLignes; i++)
for (int j=0; j < nColonnes; j++)
t[i][j] = i*j;
/* Affichage */
for (int i=0; i < nLignes; i++) {
for (int j=0; j < nColonnes; j++)
cout << t[i][j] << " ";
cout << std::endl;
}
Free_Tab();
system("pause>nul");
return 0;
}
void Free_Tab(){
for (int i=0; i < nLignes; i++)
delete[] t[i];
delete[] t;
}
6 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I
Exécution 1
Nombre de colonnes : 6
Nombre de lignes : 3
0 0 0 0 0 0
0 1 2 3 4 5
0 2 4 6 8 10
Exécution 2
Tapez la valeur de taille_i : 8
Tapez la valeur de taille_j : 2
0 0
1 1
0 2
0 3
0 4
0 5
0 6
0 7
3. Les tableaux de caractères
Un tableau de caractères constitue une chaîne de caractères. Exemple avec des caractères de huit bits:
char chaine[20] = "BONJOUR";
Les tableaux de char
Un tableau de char constitue une chaîne de caractères. Exemple :
char chaine[20] = "BONJOUR";
La variable chaine peut stocker jusqu'à 20 caractères, et contient les caractères suivants :
'B','O','N','J','O','U','R','\0'
Le dernier caractère est nul pour indiquer la fin de la chaîne de caractères. Il est donc important de prévoir son stockage dans le tableau.
Dans cet exemple, 8 caractères sont donc nécessaires.
L'exemple précédent équivaut à :
char chaine[20] = {'B','O','N','J','O','U','R','\0'};
Si la chaîne est initialisée à la déclaration, on peut également laisser le compilateur détecter le nombre de caractères nécessaires, en
omettant la dimension :
char chaine[] = "BONJOUR"; // tableau de 8 caractères
Dans ce cas la variable ne pourra stocker que des chaînes de taille inférieure ou égale à celle d'initialisation.
Les fonctions de manipulation de chaîne de caractères sont les mêmes que dans le langage C. Elles portent un nom commençant par str
(String) :
int strlen(const char* source)
Retourne la longueur de la chaîne source (sans le caractère nul final).
void strcpy(char* dest, const char* source)
7 Adner Sodrel LEPHOYO
Copie la
COURS PROGRAMMATION C++ I DIGL L2 2I chaîne
source dans
le tableau pointé par dest.
Les tableaux de char16_t and char32_t
À partir de C++11 (C++ norme de 2011) trois types de chaînes de caractères sont prise en charge: UTF-8, UTF-16, et UTF-32. Le type
char conserve ses unités de codage de huit bits pour le codage des caractères Unicode via UTF-8, les nouveaux types char16_t et
char32_t sont des unités de codage de seize ou trente-deux bits pour le codage des caractères Unicode via UTF-16 ou UTF-32.
Ces types sont standard à partir de C++2011 mais n'existent pas sur des compilateurs plus anciens, ni même sur les
compilateur C-2011. Il est également possible de les initialiser avec des littéraux, selon le cas:
u8"I'm a UTF-8 string."
u"This is a UTF-16
string." U"This is a
UTF-32 string."
Les tableaux de wchar_t
Les compilateurs moderne permettent de faire des chaînes de wchar_t (16 bits ou 32 bits) au lieu de char (8 bits). Ceci a été fait pour
permettre de représenter une plus grande partie des caractères Unicode, même si on peut représenter l'ensemble de ces caractères en UTF-
8.
Dans ce cas, il faut précéder les caractères par L :
wchar_t chaine[20] = L"BONJOUR";
Equivaut à :
wchar_t chaine[20] = {L'B',L'O',L'N',L'J',L'O',L'U',L'R',L'\0'};
Les fonctions permettant leur manipulation portent un nom similaire, excepté que str doit être remplacé par wcs (Wide Character String) :
int wcslen(const wchar_t* source)
wcscpy(wchar_t* dest, const wchar_t* source)
Portabilité
Certains environnements de développement (Visual C++ en particulier) considèrent Unicode comme une option de compilation. Cette
option de compilation définit alors le type TCHAR comme char (option legacy) ou wchar_t (option unicode), selon l'option Unicode.
Les constantes sont alors à encadrer par la macro _T(x) qui ajoutera L si nécessaire :
TCHAR chaine1[20] = _T("BONJOUR");
TCHAR chaine2[20] =
{_T('B'),_T('O'),_T('N'),_T('J'),_T('O'),_T('U'),_T('R'),_T('\0')};
Le nom des fonctions de manipulation commence par _tcs :
▪ int _tcslen(const TCHAR* source)
▪ _tcscpy(TCHAR* dest, const TCHAR* source)
II. Les fonctions
1. Utilisation des fonctions
Le C++ est un langage procédural (entre autres paradigmes), on peut définir des fonctions qui vont effectuer une
certaine tâche. On peut paramétrer des fonctions qui vont permettre de paramétrer cette tâche et rendre ainsi les
8 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I
fonctions réutilisables dans d'autres contextes.
Exemple général trivial :
#include <iostream>
using namespace std;
int doubleur(int a_locale_doubleur) // Mise en évidence facultative mais très pédagogique de
la portée locale de la variable.
{
return 2*a_locale_doubleur;
}
int main()
{
int nombre = 21;
cout << doubleur(nombre) << endl; // 42.
return 0;
}
Compléments indispensables pour les débutants
On indique devant le nom de la fonction la nature du return : int, string, char, double... (void en l'absence de
return).
Les variables définies localement restent locales. On peut ajouter au nom de la variable un affixe comme au
début de la page, avec par exemple _locale_ et le nom de la fonction, pour plus de facilité de relecture pour les
grands débutants, mais cela n'est pas habituel.
Une fonction pourra appeler d'autres fonctions et ainsi de suite.
Une fonction peut même s'appeler elle-même : on parle alors de fonctions récursives.
Limitations ("Ce ne sont pas des bugs, ce sont des fonctionnalités !") actuelles du C++ :
Le returnpeut être un tableau (c'est-a-dire que la fonction peut
renvoyer un tableau). Le return renvoie une seule valeur.
Prototype d'une fonction
Le prototype va préciser le nom de la fonction, donner le type de la valeur de retour de la fonction (void quand il n'y a
pas de retour), et donner les types des paramètres éventuels, ainsi que leurs éventuelles valeurs par défaut. Le prototype
d'une fonction est facultatif. Si on ne met pas de prototype, on devra rédiger la fonction avant le main, au lieu de la
rédiger après ou dans un fichier séparé (extension .h).
syntaxe
type identificateur(paramètres);
Exemple
// prototype de la fonction f :
double f(double x,double y);
Rôle
Le rôle d'un prototype n'est pas de définir les instructions de la fonction, mais donner sa signature. Il est utilisé pour
spécifier que la fonction existe, et est implémentée ailleurs (dans un autre fichier, une librairie, ou à la fin du fichier
source).
9 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I
2. Définition d'une fonction
La définition va reprendre le prototype mais va préciser cette fois le contenu de la fonction (le corps).
Syntaxe
type identificateur(paramètres)
{
... Liste d'instructions ...
}
Exemple
#include <iostream>
using namespace std;
// définition de la fonction f :
double f(double x, double y)
{
double a;
a = x*x + y*y;
return a;
}
int main()
{
double u, v, w;
cout << "Tapez la valeur de u : "; cin >> u;
cout << "Tapez la valeur de v : "; cin >> v;
w = f (u, v); //appel de notre fonction
cout << "Le résultat est " << w << endl;
return 0;
}
Exemple avec prototype
10 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I
#include <iostream>
using namespace std;
// prototype de la fonction f :
double f(double x, double y);
int main()
{
double u, v, w;
cout << "Tapez la valeur de u : "; cin >> u;
cout << "Tapez la valeur de v : "; cin >> v;
w = f (u, v); //appel de notre fonction
cout << "Le résultat est " << w << endl;
return 0;
}
// définition de la fonction f :
double f(double x, double y)
{
double a;
a = x*x + y*y;
return a;
}
Dans cet exemple, le prototype est nécessaire, car la fonction est définie après la fonction main qui l'utilise. Si le
prototype est omis, le compilateur signale une erreur.
3. Portée des variables
Présentation
Une fonction peut accéder :
à ses différents paramètres,
à ses variables définies localement,
aux variables globales (on évitera au maximum d'utiliser de telles variables).
11 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I
On appelle environnement d'une fonction l'ensemble des variables auxquelles elle peut accéder. Les différents
environnements sont donc largement séparés et indépendants les uns des autres. Cette séparation permet de mieux
structurer les programmes.
Exemple
1 #include <iostream>
2 using namespace std;
3
4 int b; // variable globale
5
6 double f(double x, double y)
7 {
8 double a; // variable locale à la fonction f
9 a = x*x + y*y;
10 return a;
11 }
12
13 double g(double x)
14 {
15 int r, s, t; // variables locales à la fonction g
16 /* ... */
17 }
18
19 int main()
20 {
21 double u, v, w; // variables locales à la fonction main
22 /* ... */
23 return 0;
24 }
b est une variable globale (à éviter : une structure la remplace avantageusement !).
f peut accéder
à ses paramètres
x et y. à sa
variable locale a.
à la variable
globale b. g peut
accéder
à son paramètre x.
à ses variables locales
r,s et t. à la variable
globale b.
la fonction main peut accéder
à ses variables locales
u,v et w. à la variable
globale b.
4. Passage de paramètres par pointeur
Passer un paramètre par pointeur permet de modifier la valeur pointée en utilisant l'opérateur de déréférencement *.
Pour passer un pointeur comme argument de fonction, il faut en spécifier le type dans la définition de la fonction et
(si pertinent), dans le prototype de la fonction, comme ceci :
12 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I
void passagePointeur(int *); // Prototype d'une fonction renvoyant void
// et prenant comme argument un pointeur vers un int
void passagePointeur(int * ptr) // Définition d'une fonction renvoyant void
// et prenant comme argument un pointeur vers un int appelé ptr
{
/* ... */
}
De même, il faut, lors de l'appel de fonction, non pas spécifier le nom de la variable comme lors d'un passage par
valeur, mais son adresse. Pour ceci, il suffit de placer le signe & devant la variable.
int a = 5; // Initialisation d'une variable a, de valeur 5
passageValeur( a ); // Appel d'une fonction par valeur
passagePointeur( &a ); // Appel d'une fonction par pointeur
Notez donc le & devant la variable, ceci a pour effet de passer l'adresse mémoire de la variable. Examinons le
programme simple suivant :
#include <iostream>
using std::cout;
void passagePointeur(int *);
void passageValeur(int);
int main()
{
int a = 5;
int b = 7;
cout << "a : " << a << endl;
cout << "b : " << b; // Affiche les deux variables
passageValeur (a); // Appel de la fonction en passant la variable a par valeur
// Une copie de la valeur est transmise à la fonction
passagePointeur (&b); // Appel de la fonction en passant l'adresse de la variable b par pointeur
// Une copie de l'adresse est transmise à la fonction
cout << endl;
cout << "a : " << a << endl;
cout "b : " << b; // Réaffiche les deux variables
system ("PAUSE");
return 0;
}
void passagePointeur(int * ptr)
{
int num = 100;
cout << endl;
cout "*ptr : " << *ptr; // Affiche la valeur pointée
* ptr = 9; // Change la valeur pointée;
ptr = # // <-- modification de l'adresse ignorée par la fonction appelante
}
void passageValeur(int val)
{
cout << endl;
cout "val : " << val;
val = 12; // <-- modification de la valeur ignorée par la fonction appelante
}
13 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I
On affiche les deux variables puis on appelle les deux fonctions, une par valeur et une par pointeur. Puis on affiche dans
ces deux fonctions la valeur et on modifie la valeur. De retour dans main, on réaffiche les deux variables a et b. a, qui a
été passée par valeur, n'a pas été modifiée et a toujours sa valeur initiale (5), spécifiée à l'initialisation. Or, b n'a plus la
même valeur que lors de son initialisation, 7, mais la valeur de 9. En effet, lors d'un appel par valeur, une copie de cette
valeur est créée, donc lorsqu'un appel de fonction par valeur est effectué, on ne modifie pas la valeur d'origine mais une
copie, ce qui fait que lorsqu'on retourne dans la fonction d'origine, les modifications effectuées sur la variable dans la
fonction appelée ne sont pas prises en comptes. En revanche, lors d'un appel par pointeur, il s'agit de la variable elle-
même qui est modifiée (puisqu'on passe son adresse). Donc si elle est modifiée dans la fonction appelée, elle le sera
également dans la fonction appelante.
Les avantages sont que cet appel nécessite moins de charge de travail pour la machine. En effet, par valeur, il faut faire
une copie de l'objet, alors que par pointeur, seule l'adresse de l'objet est copiée. L'inconvénient, c'est qu'on peut
accidentellement modifier dans la fonction appelée la valeur de la variable, ce qui se répercutera également dans la
fonction appelante. La solution est simple : il suffit d'ajouter const dans le prototype de fonction de cette manière :
void passagePointeur(const int *);
// En d'autres termes, un pointeur vers un int constant
Ainsi que la définition de fonction comme ceci :
void passagePointeur(const int * ptr)
{
// * ptr = 9; // <- maintenant interdit
}
En recompilant le programme mis plus haut avec ces deux choses, on constate un message d'erreur indiquant que l'on n'a pas
le droit de modifier une valeur constante.
5. Passage de paramètres par référence
Passer un paramètre par référence a les mêmes avantages que le passage d'un paramètre par pointeur. Celui-ci est
également modifiable. La différence est que l'opérateur de déréférencement n'est pas utilisé, car il s'agit déjà d'une
référence.
Le passage de paramètre par référence utilise une syntaxe similaire au passage par pointeur dans la déclaration de la
fonction, en utilisant & au lieu de
*.
Par exemple :
void incrementer(int& value)
// value : référence initialisée quand la fonction est appelée
{
value++;
// la référence est un alias de la variable passée en paramètre
}
void test()
{
int a = 5;
cout << "a = " << a << endl; // a = 5
incrementer(a);
cout << "a = " << a << endl; // a = 6
}
Le paramètre ainsi passé ne peut être qu'une variable. Sa valeur peut être modifiée dans la fonction appelée, à moins
d'utiliser le mot const :
14 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I
void incrementer(const int& value)
{
value++; // <- erreur générée par le compilateur
}
void test()
{
int a = 5;
cout << "a = " << a << endl;
incrementer(a);
cout << "a = " << a << endl;
}
La question que l'on peut se poser est puisque le passage par référence permet de modifier le paramètre, pourquoi l'en
empêcher, et ne pas utiliser un simple passage par valeur ? La réponse se trouve lorsqu'on utilise des objets ou des
structures. Le passage par valeur d'un objet ou d'une structure demande une recopie de la valeur de ses membres
(utilisant un constructeur de recopie pour les objets). L'avantage de passer une référence est d'éviter la recopie. Le mot
const permet de garantir qu'aucun membre de l'objet ou de la structure n'est modifié par la fonction.
6. Pointeur de fonction
Un pointeur de fonction stocke l'adresse d'une fonction, qui peut être appelée en utilisant ce pointeur.
La syntaxe de la déclaration d'un tel pointeur peut paraître compliquée, mais il suffit de savoir que cette déclaration est identique à celle de la
fonction pointée, excepté que le nom de la fonction est remplacé par (* pointeur).
Obtenir l'adresse d'une fonction ne nécessite pas l'opérateur &. Il suffit de donner le nom de la fonction seul.
Exemple :
#include <iostream>
#include <iomanip>
using namespace std;
int (* pf_comparateur)(int a, int b);
// peut pointer les fonctions prenant 2 entiers en arguments et retournant un entier
int compare_prix(int premier, int second)
{
return premier - second;
}
int main()
{
pf_comparateur = &compare_prix;
cout << "compare 1 et 2 : " << (*pf_comparateur)(1, 2) << endl;
return 0;
}
La syntaxe d'appel à une fonction par un pointeur est identique à l'appel d'une fonction classique.
7. Passage de fonctions en paramètre
Il s'agit en fait de passer un pointeur de
fonction. Exemple :
void sort_array( int[] data, int count, int (* comparateur_tri)(int a, int b) );
Pour clarifier la lecture du code, il est préférable d'utiliser l'instruction typedef :
15 Adner Sodrel LEPHOYO
COURS PROGRAMMATION C++ I DIGL L2 2I
typedef int (* pf_comparateur)(int a, int b);
void sort_array( int[] data, int count, pf_comparateur comparateur_tri );
16 Adner Sodrel LEPHOYO