[Plan du site]
Vous êtes ici ---
> Le Site du Zéro
> Les tutoriels
> Officiels
> Programmation
> Apprenez à programmer en C++ ! > [Théorie] La Programmation Orientée Objet > Nouveautés pour les fonctions
> Lecture du tutoriel
Nouveautés pour les fonctions
Nous avons vu que le C++ proposait de nombreuses nouveautés relatives pour les variables.
Ce chapitre est la suite du précédent, mais est cette fois axé sur les nouveautés relatives aux fonctions.
Ce chapitre sera un peu plus court car il y a assez peu de changements au final. Ne vous endormez pas pour autant parce que vous allez découvrir les valeurs par défaut et les fonctions surchargées, deux éléments très importants que nous réutiliserons largement dans la suite.
Courage, c'est le dernier chapitre avant la POO (
ou plutôt devrais-je dire : "Profitez-en bien mes petits !
").
Si je vous dis "paramètre de fonction", vous voyez de quoi je parle n'est-ce pas ?
Je l'espère, parce qu'il serait temps de le savoir à votre niveau maintenant
Bon allez, un petit rappel !
Comme un petit rappel ne fait jamais de mal, voici un exemple de fonction :
Code : C++ 1
2
3
4
5
6
7
8
9
10 | int nombreDeSecondes(int heures, int minutes, int secondes)
{
int total = 0;
total = heures * 60 * 60;
total += minutes * 60;
total += secondes;
return total;
}
|
Cette fonction calcule le nombre de secondes en additionnant les heures, minutes et secondes qu'on lui envoie. Rien de bien compliqué
Les variables
heures,
minutes et
secondes sont les
paramètres de la fonction nombreDeSecondes. Ce sont des valeurs qu'elle reçoit, celles avec lesquelles elle va travailler.
Il est facile de reconnaître les paramètres d'une fonction, car ceux-ci se trouvent toujours écrits entre les parenthèses
Les valeurs par défaut
La nouveauté en C++, c'est qu'on peut donner des valeurs par défaut à certains paramètres de nos fonctions. Ainsi, on ne sera pas obligé d'indiquer à chaque fois tous les paramètres lorsqu'on appelle une fonction !
Pour bien voir comment on doit procéder, on va regarder le code complet. J'aimerais que vous le copiez dans votre IDE pour faire les tests en même temps que moi :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | #include <iostream>
using namespace std;
// Prototype de la fonction
int nombreDeSecondes(int heures, int minutes, int secondes);
// Main
int main()
{
cout << nombreDeSecondes(1, 10, 25) << endl;
return 0;
}
// Définition de la fonction
int nombreDeSecondes(int heures, int minutes, int secondes)
{
int total = 0;
total = heures * 60 * 60;
total += minutes * 60;
total += secondes;
return total;
}
|
Ce code donne le résultat suivant :
Code : Console
Sachant qu'1 heure = 3600s, 10 minutes = 600s, 25 secondes =... 25s, le résultat est logique car 3600 + 600 + 25 = 4225

Bref, tout va bien.
Maintenant supposons que l'on veuille rendre certains paramètres facultatifs, par exemple parce qu'on utilise en pratique plus souvent les heures que le reste.
On va devoir modifier le prototype de la fonction (et non sa définition, attention).
Indiquez la valeur par défaut que vous voulez donner aux paramètres si on ne les a pas renseigné lors de l'appel de la fonction :
Code : C++1 | int nombreDeSecondes(int heures, int minutes = 0, int secondes = 0);
|
Dans cet exemple, seul le paramètre heures sera obligatoire, les deux autres étant désormais facultatifs. Si on ne renseigne pas les minutes et les secondes, les variables vaudront alors 0 dans la fonction.
Voici le code complet que vous devriez avoir sous les yeux :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | #include <iostream>
using namespace std;
// Prototype avec les valeurs par défaut
int nombreDeSecondes(int heures, int minutes = 0, int secondes = 0);
// Main
int main()
{
cout << nombreDeSecondes(1, 10, 25) << endl;
return 0;
}
// Définition de la fonction, SANS les valeurs par défaut
int nombreDeSecondes(int heures, int minutes, int secondes)
{
int total = 0;
total = heures * 60 * 60;
total += minutes * 60;
total += secondes;
return total;
}
|
Si vous avez lu attentivement ce code, vous avez dû vous rendre compte de quelque chose :
les valeurs par défaut sont spécifiés uniquement dans le prototype, PAS dans la définition de la fonction ! On se fait souvent avoir, je vous préviens

Si vous vous trompez, le compilateur vous indiquera une erreur à la ligne de la définition de la fonction.
Bon, ce code ne change pas beaucoup du précédent. A part les valeurs par défaut dans le prototype, rien n'a été modifié (et le résultat à l'écran sera toujours le même).
La nouveauté maintenant, c'est qu'on peut supprimer des paramètres lors de l'appel de la fonction (ici dans le
main). On peut par exemple écrire :
Code : C++1 | cout << nombreDeSecondes(1) << endl;
|
Le compilateur lit les paramètres de gauche à droite. Comme il n'y en a qu'un et que seules les heures sont obligatoires, il devine que la valeur "1" correspond à un nombre d'heures.
Le résultat à l'écran sera le suivant :
Code : Console
Mieux encore, vous pouvez indiquer juste les heures et les minutes si vous le désirez :
Code : C++1 | cout << nombreDeSecondes(1, 10) << endl;
|
Code : Console
Du temps que vous indiquez au moins les paramètres obligatoires, il n'y a pas de problème
Cas particuliers, attention danger
Bon, mine de rien il y a quand même quelques pièges, ce n'est pas si simple que ça

On va voir ces pièges sous la forme de questions / réponses :
Et si je veux envoyer à la fonction juste les heures et les secondes, mais pas les minutes ?
Tel quel, c'est impossible. En effet, je vous l'ai dit plus haut, le compilateur va analyser les paramètres de gauche à droite. Le premier correspondra forcément aux heures, le second aux minutes et le troisième aux secondes.
Vous ne pouvez PAS écrire :
Code : C++1 | cout << nombreDeSecondes(1,,25) << endl;
|
C'est interdit. Si vous le faites, le compilateur vous fera comprendre qu'il n'apprécie guère vos manoeuvres. C'est comme ça : en C++, on ne peut pas "sauter" des paramètres, même s'ils sont facultatifs. Si vous voulez indiquer le premier et le dernier paramètre, il vous faudra obligatoirement spécifier ceux du milieu. On devra donc écrire :
Code : C++1 | cout << nombreDeSecondes(1, 0, 25) << endl;
|
Est-ce que je peux rendre juste les heures facultatives, et rendre les minutes et secondes obligatoires ?
Si le prototype est défini dans le même ordre que tout à l'heure : non.
Les paramètres facultatifs doivent obligatoirement se trouver à la fin (à droite).
Ce code ne compilera donc pas :
Code : C++1
2 | int nombreDeSecondes(int heures = 0, int minutes, int secondes);
// Erreur, les paramètres par défaut doivent être à droite
|
La solution, pour régler ce problème, consiste à placer le paramètre
heures à la fin :
Code : C++1
2 | int nombreDeSecondes(int secondes, int minutes, int heures = 0);
// OK
|
Est-ce que je peux rendre tous mes paramètres facultatifs ?
Oui, ça ne pose pas de problème :
Code : C++1 | int nombreDeSecondes(int heures = 0, int minutes = 0, int secondes = 0);
|
Dans ce cas, l'appel de la fonction pourra être fait comme ceci :
Code : C++1 | cout << nombreDeSecondes() << endl;
|
Le résultat retourné sera bien entendu 0 dans notre cas
Règles à retenir
En résumé, il y a 2 règles que vous devez retenir pour les valeurs par défaut :
- Seul le prototype doit contenir les valeurs par défaut (pas la définition de la fonction).
- Les valeurs par défaut doivent se trouver à la fin de la liste des paramètres ("à droite").
Ca, c'est probablement
la nouveauté la plus importante des fonctions ! Cela nous aidera énormément lorsque nous ferons de la POO un peu plus loin
De quoi s'agit-il ? D'un nouveau système en C++ qui permet de
surcharger des fonctions.
En gros, et pour faire simple, c'est une technique qui nous permet de créer plusieurs fonctions ayant
le même nom... sans que le compilateur crie au loup
La signature d'une fonction
Avant toute chose, il faut que je vous parle de ce qu'on appelle la
signature d'une fonction. C'est un peu sa carte d'identité, ce qui permet au compilateur de différencier les fonctions entre elles.
Chaque fonction est constituée de 3 éléments, ni plus ni moins :
- Un type de retour
- Un nom
- Une liste de paramètres
On va représenter ça sur un schéma pour être sûr qu'on voie bien la même chose
Bon, qu'est-ce qui permet d'identifier une fonction d'après vous ? Comment le compilateur fait-il pour vérifier si une fonction est bien différente d'une autre ?
En C, le compilateur se basait sur le nom, et uniquement sur le nom. Si 2 fonctions avaient le même nom, la compilation plantait. L'identification était donc faite sur le nom.
En C++, le compilateur se base sur
le nom et les paramètres ! On peut avoir du coup 2 fonctions avec le même nom, à condition que celles-ci reçoivent des paramètres différents.
Le nom et les paramètres de la fonction constituent ce qu'on appelle la
signature de la fonction. C'est ce qui permet au compilateur de l'identifier en C++.
Le type de retour n'est donc pas pris en compte pour identifier la fonction.
La surcharge d'une fonction
La surcharge consiste à créer des fonctions qui ont le même nom, mais qui ont des paramètres différents (donc une signature différente).
Voici ce qui peut varier :
- Le nombre de paramètres
- Le type de chacun de ces paramètres
Encore une fois, je le rappelle, le nom que l'on donne à chacun des paramètres, le compilo il s'en fout complètement
Prenons un exemple pour bien comprendre ce que ça va nous permettre de faire. Imaginez une fonction addition. On peut additionner des entiers (int), mais aussi des décimaux (double).
En C, il aurait fallu nommer les deux fonctions différemment (par exemple sommeEntiers et sommeDecimaux). En C++, on peut leur donner le même nom et ça va grandement simplifier leur utilisation, vous allez voir.
Code : C++1
2
3
4
5
6
7
8
9 | int somme(int nb1, int nb2)
{
return nb1 + nb2;
}
double somme(double nb1, double nb2)
{
return nb1 + nb2;
}
|
Les prototypes de ces fonctions sont donc :
Code : C++1
2 | int somme(int nb1, int nb2);
double somme(double nb1, double nb2);
|
Leurs signatures sont :
Code : C++1
2 | somme(int, int)
somme(double, double)
|
Ces fonctions ont des signatures différentes et portent le même nom.
Ce sont des fonctions surchargées
Maintenant, dans le main on peut faire appel à la fonction somme pour additionner indifféremment des entiers ou des décimaux. C'est le compilateur qui décide quelle fonction il appelle en fonction du nombre et du type des paramètres.
Voici un code complet que vous pouvez tester :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | #include <iostream>
using namespace std;
int somme(int nb1, int nb2);
double somme(double nb1, double nb2);
int main()
{
cout << somme(10, 15) << endl << somme(2.5, 0.3) << endl;
return 0;
}
int somme(int nb1, int nb2)
{
return nb1 + nb2;
}
double somme(double nb1, double nb2)
{
return nb1 + nb2;
}
|
Résultat :
Code : Console
On a appelé 2 fois la fonction "somme", mais c'est en fait une fonction différente qui a été appelée à chaque fois
Vous pouvez surcharger la fonction autant de fois que vous le désirez. On pourrait donc aussi rajouter par exemple la fonction qui fait la somme d'un entier et d'un décimal :
Code : C++1
2
3
4 | double somme(int nb1, double nb2)
{
return nb1 + nb2;
}
|
... ou encore celle qui fait la somme de 3 entiers :
Code : C++1
2
3
4 | int somme(int nb1, int nb2, int nb3)
{
return nb1 + nb2 + nb3;
}
|
Les possibilités sont infinies
Bien entendu, on fait de la surcharge de fonction pour des choses plus "intéressantes" que des sommes, mais ça on le découvrira petit à petit en fonction de nos besoins.
Ce que nous allons voir ici ressemble à beaucoup aux macros (relisez le
chapitre sur le préprocesseur si vous avez oublié ce que c'est

).
Les macros sont un bon moyen, utilisées intelligemment, d'accélérer la vitesse d'exécution du programme si certains bouts de code sont souvent réutilisés.
Toutefois, les macros sont assez délicates à manipuler et impliquent l'utilisation du préprocesseur.
En C++, on a inventé le mot-clé
inline qui permet de faire, grosso modo, la même chose que les macros sans cette fois passer par le préprocesseur. C'est donc le compilateur qui se charge de faire le "remplacement de code" au moment de la compilation. L'avantage, c'est qu'on peut faire plus de vérifications (notamment sur les types des paramètres).
Exemple d'utilisation d'une fonction inline
Prenons l'exemple suivant (on le discutera ensuite) :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13 | inline int carre(int nombre);
int main()
{
cout << carre(10) << endl;
return 0;
}
inline int carre(int nombre)
{
return nombre * nombre;
}
|
Vous voyez que j'ai ajouté le mot-clé
inline au début du prototype ET au début de la définition de la fonction. Cela signifie pour le compilateur "
A chaque fois qu'on fera appel à la fonction carre, je placerai directement le code de cette fonction à l'endroit de l'appel".
En clair, après compilation voici ce qu'il restera dans votre exécutable :
Code : C++1
2
3
4
5
6 | int main()
{
cout << 10 * 10 << endl;
return 0;
}
|
La fonction inline disparaît complètement après compilation. Tout son code se trouve placé à l'endroit de l'appel (la ligne du cout dans notre cas).
L'avantage est que l'exécution du programme sera plus rapide, surtout si la fonction est appelée plusieurs fois. En effet, lors d'un appel "classique" de fonction, le processeur va sauter à l'adresse de la fonction en mémoire, retenir l'adresse où il en était pour revenir à la fonction appelante une fois l'autre fonction terminée... Bref, c'est très rapide, mais si la fonction est amenée à être appelée très souvent, il est préférable d'en faire une inline (on dit l'
inliner 
) pour éviter de répéter tout ce processus.
Le défaut, c'est que le programme risque de grossir un peu une fois compilé (le même code étant répété dans l'exécutable). Mais bon, en général cette différence est quand même négligeable
En règle générale, les fonctions inline sont donc des fonctions très courtes que l'on est susceptible de réutiliser souvent, comme c'est le cas de la fonction carre ici.
En pratique, on utilise quand même assez peu les fonctions inline, sachez-le (c'est comme les macros, je ne pense pas que vous vous en soyez beaucoup servis jusqu'ici

). Ca reste cependant une des nouveautés du C++ relatives aux fonctions que je devais vous présenter
pssst, puisqu'on y est, serez-vous capables de surcharger la fonction inlinée carre pour qu'elle calcule le carré d'un nombre décimal ?
Bon, vous êtes capables de surcharger des fonctions inlinées avec des paramètres par défaut, qu'est-ce qui pourrait bien vous faire peur maintenant ?
Oh, mais ne faites pas les malins, tout ceci n'était qu'une misérable mise en bouche comparé à ce qui vous attend
En effet, dans le prochain chapitre on va rentrer en plein dans le coeur du C++ : on va découvrir la
programmation orientée objet. Bien sûr, on va y aller pas à pas, en douceur, sinon ça risque d'être un peu... violent
Quand vous êtes prêts, rendez-vous au proch... bon, je suis déjà dans le chapitre suivant moi, qu'est-ce que vous attendez ?