Introduction aux fonctions en C++
La segmentation d'un programme en morceaux de code gérables est fondamentale pour la programmation dans tous les langages. Une fonction est un bloc de construction de base dans les programmes C++. Jusqu'à présent, chaque exemple avait une fonction, main(), et utilisait généralement des fonctions de la bibliothèque standard. Dans ce cours, vous apprendrez à définir vos propres fonctions.
Une application C++ se compose de nombreuses fonctions, chacune fournissant une capacité distincte bien définie. L'exécution commence dans main(), qui doit être défini dans l'espace de noms global. main() appelle d'autres fonctions, chacune pouvant appeler d'autres fonctions, et ainsi de suite. Les fonctions autres que main() peuvent être définies dans un espace de noms que vous créez.
Définir des fonctions
Une fonction est un bloc de code autonome avec un objectif spécifique. Les définitions de fonctions ont en général la même structure de base que main(). Une définition de fonction consiste en un en-tête de fonction suivi d'un bloc contenant le code de la fonction. L'en-tête de fonction spécifie trois choses:
- Le type de retour, qui est le type de valeur, le cas échéant, que la fonction renvoie à la fin de son exécution. Une fonction peut renvoyer des données de n'importe quel type, y compris des types fondamentaux, des types de classe, des types de pointeur ou des types de référence. Il peut également ne rien renvoyer, auquel cas vous spécifiez le type de retour comme void.
- Le nom de la fonction. Les fonctions sont nommées selon les mêmes règles que les variables.
- Le nombre et les types d'éléments de données qui peuvent être passés à la fonction lorsqu'elle est appelée. C'est ce qu'on appelle la liste des paramètres, et elle apparaît sous la forme d'une liste séparée par des virgules entre parenthèses après le nom de la fonction.
Syntaxe
type_retour nom_fonction(liste_parametres) { // Corps de la fonction... }
Exemple 1
La fonction suivante calcule le périmètre d'un rectangle défini par 2*(longueur + largeur)
double perimetre(double largeur, double longueur) { double res; res=2*(largeur+longueur); return res; }
Si rien ne doit être passé à une fonction lorsqu'elle est appelée, alors rien n'apparaît entre les parenthèses. S'il y a plus d'un élément dans la liste des paramètres, ils sont séparés par des virgules. La fonction perimetre() définie ci-dessus a deux paramètres, largeur et longueur. Les noms de paramètres sont utilisés dans le corps de la fonction pour accéder aux valeurs correspondantes qui ont été transmises à la fonction. La fonction perimètre() pourrait être appelée depuis n'importe où dans le programme comme suit :
double lar {3.4}; const double resultat=perimetre(lar,2.7);
Lorsque nous appelons la fonction perimetre() alors elle est évaluée, le code dans le corps de la fonction est exécuté avec les paramètres largeur et longueur initialisés à 3.4 et 2.7. Le terme argument est utilisé pour les valeurs transmises à une fonction lorsqu'elle est appelée. Ainsi, dans notre exemple, lar et 2.7 sont des arguments, et largeur et longueur sont les paramètres correspondants. La séquence d'arguments dans un appel de fonction doit correspondre à la séquence des paramètres dans la liste de paramètres de la définition de fonction. Plus précisément, leurs types doivent correspondre. S'ils ne correspondent pas exactement, le compilateur appliquera des conversions implicites si cela est possible.
La combinaison du nom de la fonction et de la liste des paramètres est appelée la signature d'une fonction. Le compilateur utilise la signature pour décider quelle fonction doit être appelée dans une instance particulière. Ainsi, les fonctions qui ont le même nom doivent avoir des listes de paramètres qui diffèrent d'une certaine manière pour permettre de les distinguer.
La fonction perimetre() a renvoyé une valeur de type double. Cependant, toutes les fonctions ne doivent pas nécessairement renvoyer une valeur - elles peuvent simplement écrire quelque chose dans un fichier ou une base de données ou modifier un état global ou afficher quelque chose à l'écran. Le mot-clé void est utilisé pour spécifier qu'une fonction ne renvoie pas de valeur comme suit :
Exemple 2
void afficher(int a) { cout << "a : " << a; }
Les variables que vous définissez dans le corps d'une fonction et tous les paramètres sont locaux à la fonction. Vous pouvez utiliser les mêmes noms dans d'autres fonctions à des fins très différentes. La portée de chaque variable que vous définissez dans une fonction s'étend du point auquel elle est définie jusqu'à la fin du bloc qui la contient. Les seules exceptions à cette règle sont les variables que vous définissez comme statiques, dont nous parlerons plus tard.
Valeur de retour
Une fonction avec un type de retour autre que void doit retourner une valeur du type spécifié dans l'en-tête de la fonction. La seule exception à cette règle est la fonction main(), où, comme vous le savez, atteindre l'accolade fermante équivaut à renvoyer 0. Normalement, cependant, la valeur de retour est calculée dans le corps de la fonction et est renvoyée par l'instruction return, qui termine la fonction, et l'exécution continue à partir du point d'appel.
L'instruction return de la fonction précédente perimetre() renvoie la valeur de res au point où la fonction a été appelée. La variable res est locale à la fonction et cesse d'exister lorsque la fonction finit de s'exécuter, alors comment est-elle renvoyée ? La réponse est qu'une copie du double renvoyé est faite automatiquement, et cette copie est mise à la disposition de la fonction appelante. La forme générale de l'instruction return est la suivante :
Syntaxe
return expression;
expression doit évaluer à une valeur du type spécifié pour la valeur de retour dans l'en-tête de fonction ou doit être convertible à ce type. expression peut être n'importe quoi, tant qu'elle produit une valeur d'un type approprié. Il peut inclure des appels de fonction et peut même inclure un appel de la fonction dans laquelle il apparaît (récursivité).
Si le type de retour est spécifié comme étant void, aucune expression ne peut apparaître dans return. Elle doit être écrite simplement comme suit :
Syntaxe
return;
Si la dernière instruction du corps d'une fonction s'exécute de telle sorte que l'accolade fermante est atteinte, cela équivaut à l'exécution d'une instruction return sans expression. Dans une fonction dont le type de retour n'est pas void, il s'agit d'une erreur, et la fonction ne sera pas compilée - sauf main(), bien sûr.
Return est une instruction de saut qui provoque la sortie de la fonction et le retour de la valeur spécifiée à l'endroit où la fonction a été appelée. L'instruction return peut également être utilisée dans les fonctions void pour sortir avant que la fin du bloc de fonctions ne soit atteinte.
Déclarations de fonctions
Un point important à garder à l'esprit en C++ est que les fonctions doivent être déclarées avant de pouvoir être appelées. Cela ne signifie pas que la fonction doit être implémentée avant d'être appelée. Cela signifie seulement que l'en-tête de la fonction doit être spécifié au début du fichier source, afin que le compilateur sache que la fonction existe. Ce type de déclaration est connu sous le nom de prototype.
Un prototype de fonction est une déclaration qui décrit une fonction suffisamment pour que le compilateur puisse compiler les appels à cette fonction. Il définit le nom de la fonction, son type de retour et sa liste de paramètres. Une fonction ne peut être compilée que si l'appel est précédé d'une déclaration de fonction dans le fichier source.
Exemple 3
#include <iostream> using namespace std; double perimetre(double largeur, double longueur); // prototype int main(void){ double lar {3.4}; const double resultat=perimetre(lar,2.7); cout << resultat; return 0; } double perimetre(double lar, double lon) // définition de la fonction { double res; res=2*(lar+lon); return res; }
Le prototype de fonction présenté plus haut est identique à l'en-tête de la fonction avec un point-virgule ajouté. Un prototype de fonction se termine toujours par un point-virgule, mais en général, il n'est pas nécessaire qu'il soit identique à l'en-tête de la fonction. Vous pouvez utiliser des noms différents pour les paramètres par rapport à ceux utilisés dans la définition de la fonction (mais pas de types différents, bien sûr).
Il n'est pas nécessaire d'inclure les noms des paramètres dans le prototype. Seuls les types de données doivent être spécifiés. Cependant, le fait d'inclure les noms constitue une sorte de documentation, c'est donc une bonne pratique de les inclure.
double perimetre(double, double);
Ce pourrait être une bonne idée de toujours écrire des prototypes pour chaque fonction définie dans un fichier source - à l'exception de main(), bien sûr, qui ne nécessite jamais de prototype. Le fait de spécifier les prototypes au début du fichier élimine la possibilité d'erreurs de compilation dues à une séquence de fonctions inadéquate. Cela permet également aux autres programmeurs d'avoir un aperçu de la fonctionnalité de votre code.