Fonctions essentielles en C++

C++C++Beginner
Pratiquer maintenant

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Dans ce laboratoire, vous allez apprendre les fonctions en C++. Vous allez apprendre à définir et à appeler des fonctions, et à passer des arguments à des fonctions.

Aperçu du contenu

Parfois, une certaine partie du code doit être utilisée plusieurs fois. Il est préférable de la placer dans une « sous - routine » - fonction, et d'appeler cette fonction plusieurs fois - pour faciliter la maintenance et la compréhension.

Deux parties sont impliquées dans l'utilisation d'une fonction : un appelant qui appelle la fonction, et la fonction appelée. L'appelant passe des arguments à la fonction. La fonction reçoit ces arguments, effectue les opérations programmées dans le corps de la fonction, et renvoie un résultat auquel l'appelant peut accéder.

Utilisation des fonctions

Supposons que nous devions calculer l'aire d'un cercle plusieurs fois. Il est préférable d'écrire une fonction appelée getArea() et de la réutiliser selon les besoins.

/* Test Function */
#include <iostream>
using namespace std;
const int PI = 3.14159265;

// Prototype de fonction (Déclaration de fonction)
double getArea(double radius);

int main() {
   double radius1 = 1.1, area1, area2;
   // Appel de la fonction getArea()
   area1 = getArea(radius1);
   cout << "area 1 est " << area1 << endl;
   // Appel de la fonction getArea()
   area2 = getArea(2.2);
   cout << "area 2 est " << area2 << endl;
   // Appel de la fonction getArea()
   cout << "area 3 est " << getArea(3.3) << endl;
}

// Définition de fonction
// Retourne l'aire d'un cercle connaissant son rayon
double getArea(double radius) {
   return radius * radius * PI;
}

Sortie :

area 1 est 3.63
area 2 est 14.52
area 3 est 32.67
image desc
image desc

En C++, vous devez déclarer un prototype de fonction (avant d'utiliser la fonction), et fournir une définition de fonction, avec un corps contenant les opérations programmées.

La syntaxe de la définition de fonction est la suivante :

returnValueType functionName ( parameterList ) {
   functionBody ;
}

Un prototype de fonction indique au compilateur l'interface de la fonction, c'est-à-dire le type de retour, le nom de la fonction et la liste des types de paramètres (le nombre et le type des paramètres). La fonction peut maintenant être définie n'importe où dans le fichier. Par exemple,

// Prototype de fonction - placé avant d'utiliser la fonction.
double getArea(double);  // sans le nom du paramètre
double getArea(double radius);  // les noms de paramètres sont ignorés, mais servent à la documentation

Le type de retour "void"

Sans la nécessité de renvoyer une valeur à l'appelant, vous pouvez déclarer son type de valeur de retour comme void. Dans le corps de la fonction, vous pouvez utiliser une instruction "return;" sans valeur de retour pour renvoyer le contrôle à l'appelant.

Paramètres actuels vs. paramètres formels

Dans l'exemple ci-dessus, la variable (double radius) déclarée dans la signature de getArea(double radius) est connue sous le nom de paramètre formel. Sa portée est dans le corps de la fonction. Lorsque la fonction est appelée par un appelant, l'appelant doit fournir des paramètres appelés paramètres actuels (ou arguments), dont la valeur est ensuite utilisée pour le calcul effectif. Par exemple, lorsque la fonction est appelée via "area1 = getArea(radius1)", radius1 est le paramètre actuel, avec une valeur de 1.1.

Portée des variables locales de fonction et des paramètres

Toutes les variables, y compris les paramètres de fonction, déclarées à l'intérieur d'une fonction sont disponibles uniquement pour la fonction. Elles sont créées lorsque la fonction est appelée et libérées (détruites) après que la fonction ait retourné. Elles sont appelées variables locales car elles sont locales à la fonction et ne sont pas disponibles en dehors de la fonction.

Arguments par défaut

C++ introduit les soi-disant arguments par défaut pour les fonctions. Ces valeurs par défaut seront utilisées si l'appelant omet l'argument actuel correspondant lors de l'appel de la fonction. Les arguments par défaut sont spécifiés dans le prototype de fonction et ne peuvent pas être répétés dans la définition de fonction. Les arguments par défaut sont résolus selon leur position. Par conséquent, ils ne peuvent être utilisés que pour substituer les arguments trailing pour éviter l'ambiguité. Par exemple,

/* Test Function default arguments */
#include <iostream>
using namespace std;

// Prototype de fonction - Spécifiez les arguments par défaut ici
int fun1(int = 1, int = 2, int = 3);
int fun2(int, int, int = 3);

int main() {
   cout << fun1(4, 5, 6) << endl; // Aucun argument par défaut
   cout << fun1(4, 5) << endl;    // 4, 5, 3(par défaut)
   cout << fun1(4) << endl;       // 4, 2(par défaut), 3(par défaut)
   cout << fun1() << endl;        // 1(par défaut), 2(par défaut), 3(par défaut)

   cout << fun2(4, 5, 6) << endl; // Aucun argument par défaut
   cout << fun2(4, 5) << endl;    // 4, 5, 3(par défaut)
   // cout << fun2(4) << endl;
   // erreur : trop peu d'arguments pour la fonction 'int fun2(int, int, int)'
}

int fun1(int n1, int n2, int n3) {
   // ne peut pas répéter les arguments par défaut dans la définition de fonction
   return n1 + n2 + n3;
}

int fun2(int n1, int n2, int n3) {
   return n1 + n2 + n3;
}

Sortie :

15
12
9
6
15
12
image desc

Surcharge de fonctions

C++ introduit la surcharge de fonctions (ou polymorphisme de fonctions), qui vous permet d'avoir plusieurs versions du même nom de fonction, différenciées par la liste de paramètres (nombre, type ou ordre des paramètres). Les fonctions surchargées ne peuvent pas être différenciées par le type de retour (erreur de compilation). La version correspondant à la liste d'arguments de l'appelant sera sélectionnée pour l'exécution. Par exemple,

/* Test Function Overloading */
#include <iostream>
using namespace std;

void fun(int, int, int);  // Version 1
void fun(double, int);          // Version 2
void fun(int, double);          // Version 3

int main() {
   fun(1, 2, 3);   // version 1
   fun(1.0, 2);    // version 2
   fun(1, 2.0);    // version 3
   fun(1.1, 2, 3); // version 1 - double 1.1 converti en int 1 (sans avertissement)

   // fun(1, 2, 3, 4);
      // erreur : aucune fonction correspondante pour l'appel 'fun(int, int, int, int)'
   // fun(1, 2);
      // erreur : appel de la fonction surchargée 'fun(int, int)' est ambigu
      // note : les candidats sont :
      //    void fun(double, int)
      //    void fun(int, double)
   // fun(1.0, 2.0);
      // erreur : appel de la fonction surchargée 'fun(double, double)' est ambigu
}

void fun(int n1, int n2, int n3) {  // version 1
   cout << "version 1" << endl;
}

void fun(double n1, int n2) { // version 2
   cout << "version 2" << endl;
}

void fun(int n1, double n2) { // version 3
   cout << "version 3" << endl;
}

Sortie :

version 1
version 2
version 3
version 1
image desc

Fonctions et tableaux

Vous pouvez également passer des tableaux à une fonction. Cependant, vous devez également passer la taille du tableau à la fonction. C'est parce qu'il n'est pas possible de déterminer la taille du tableau à partir de l'argument de tableau à l'intérieur de la fonction appelée. Par exemple,

/* Function to compute the sum of an array */
#include <iostream>
using namespace std;

// Prototype de fonction
int sum(int array[], int size);    // Il est également nécessaire de passer la taille du tableau
void print(int array[], int size);

// Testeur
int main() {
   int a1[] = {8, 4, 5, 3, 2};
   print(a1, 5);   // {8,4,5,3,2}
   cout << "sum is " << sum(a1, 5) << endl;  // sum is 22
}

// Définition de fonction
// Retourne la somme du tableau donné
int sum(int array[], int size) {
   int sum = 0;
   for (int i = 0; i < size; ++i) {
      sum += array[i];
   }
   return sum;
}

// Affiche le contenu du tableau donné
void print(int array[], int size) {
   cout << "{";
   for (int i = 0; i < size; ++i) {
      cout << array[i];
      if (i < size - 1) {
         cout << ",";
      }
   }
   cout << "}" << endl;
}

Sortie :

{8,4,5,3,2}
sum is 22
image desc

Passage par valeur vs. passage par référence

Il existe deux manières dont un paramètre peut être passé à une fonction : le passage par valeur et le passage par référence.

Passage par valeur

En passage par valeur, une "copie" de l'argument est créée et passée à la fonction. La fonction appelée travaille sur la "clône" et ne peut pas modifier la copie d'origine. En C/C++, les types fondamentaux (tels que int et double) sont passés par valeur.

/* Fundamental types are passed by value into Function */
#include <iostream>
using namespace std;

// Prototype de fonction
int inc(int number);

// Testeur
int main() {
   int n = 8;
   cout << "Avant l'appel de la fonction, n est " << n << endl; // 8
   int result = inc(n);
   cout << "Après l'appel de la fonction, n est " << n << endl;  // 8
   cout << "result est " << result << endl;                // 9
}

// Définition de fonction
// Retourne number+1
int inc(int number) {
   ++number;  // Modifie le paramètre, sans effet sur l'appelant
   return number;
}

Sortie :

Avant l'appel de la fonction, n est 8
Après l'appel de la fonction, n est 8
result est 9
image desc

Passage par référence

D'un autre côté, en passage par référence, une référence de la variable de l'appelant est passée à la fonction. En d'autres termes, la fonction appelée travaille sur les mêmes données. Si la fonction appelée modifie le paramètre, la même copie de l'appelant sera également modifiée. En C/C++, les tableaux sont passés par référence. C/C++ ne permet pas aux fonctions de retourner un tableau.

/* Function to increment each element of an array */
#include <iostream>
using namespace std;

// Prototype de fonction
void inc(int array[], int size);
void print(int array[], int size);

// Testeur
int main() {
   int a1[] = {8, 4, 5, 3, 2};

   // Avant l'incrémentation
   print(a1, 5);   // {8,4,5,3,2}
   // Effectuer l'incrémentation
   inc(a1, 5);     // Le tableau est passé par référence (ayant un effet de bord)
   // Après l'incrémentation
   print(a1, 5);   // {9,5,6,4,3}
}

// Définition de fonction

// Incrémente chaque élément du tableau donné
void inc(int array[], int size) {  // array[] n'est pas const
   for (int i = 0; i < size; ++i) {
      array[i]++;  // effet de bord
   }
}

// Affiche le contenu du tableau donné
void print(int array[], int size) {
   cout << "{";
   for (int i = 0; i < size; ++i) {
      cout << array[i];
      if (i < size - 1) {
         cout << ",";
      }
   }
   cout << "}" << endl;
}

Sortie :

{8,4,5,3,2}
{9,5,6,4,3}
image desc

Paramètres de fonction const

Utilisez const dès que possible lors du passage de références car cela vous empêche de modifier involontairement les paramètres et vous protège contre de nombreuses erreurs de programmation.

Dans une recherche linéaire, la clé de recherche est comparée avec chaque élément du tableau de manière linéaire. S'il y a une correspondance, elle renvoie l'index de l'élément correspondant ; sinon, elle renvoie -1. La recherche linéaire a une complexité de O(n).

/* Search an array for the given key using Linear Search */
#include <iostream>
using namespace std;

int linearSearch(const int a[], int size, int key);

int main() {
   const int SIZE = 8;
   int a1[SIZE] = {8, 4, 5, 3, 2, 9, 4, 1};

   cout << linearSearch(a1, SIZE, 8) << endl;  // 0
   cout << linearSearch(a1, SIZE, 4) << endl;  // 1
   cout << linearSearch(a1, SIZE, 99) << endl; // 8 (non trouvé)
}

// Search the array for the given key
// If found, return array index [0, size-1]; otherwise, return size
int linearSearch(const int a[], int size, int key) {
   for (int i = 0; i < size; ++i) {
      if (a[i] == key) return i;
   }
   // a[0] = 1;
   // Cela entraînera une erreur car a[] est const, ce qui signifie en lecture seule
   return -1;
}

Sortie :

0
1
-1
image desc

Passage par référence via des paramètres de "référence"

Vous pouvez passer un paramètre de type fondamental par référence via le paramètre de référence désigné par &.

/* Test Pass-by-reference for fundamental-type parameter
   via reference declaration */
#include <iostream>
using namespace std;

int squareByValue (int number);        // Pass-by-value
void squareByReference (int &number); // Pass-by-reference

int main() {
   int n1 = 8;
   cout << "Avant l'appel, la valeur est " << n1 << endl;  // 8
   cout << squareByValue(n1) << endl;  // pas d'effet de bord
   cout << "Après l'appel, la valeur est " << n1 << endl;   // 8

   int n2 = 9;
   cout << "Avant l'appel, la valeur est " << n2 << endl;  // 9
   squareByReference(n2);  // effet de bord
   cout << "Après l'appel, la valeur est " << n2 << endl;   // 81
}

// Passer le paramètre par valeur - pas d'effet de bord
int squareByValue (int number) {
   return number * number;
}

// Passer le paramètre par référence en le déclarant comme référence (&)
// - avec effet de bord sur l'appelant
void squareByReference (int &number) {
   number = number * number;
}

Sortie :

Avant l'appel, la valeur est 8
64
Après l'appel, la valeur est 8
Avant l'appel, la valeur est 9
Après l'appel, la valeur est 81
image desc

###2.8 Fonctions mathématiques

C++ fournit de nombreuses fonctions mathématiques couramment utilisées dans la bibliothèque <cmath>

sin(x), cos(x), tan(x), asin(x), acos(x), atan(x) :
   Prennent un type d'argument et un type de retour de float, double, long double.
sinh(x), cosh(x), tanh(x) :
   Fonctions trigonométriques hyperboliques.
pow(x, y), sqrt(x) :
   Puissance et racine carrée.
ceil(x), floor(x) :
   Renvoient l'entier supérieur et l'entier inférieur du nombre à virgule flottante.
fabs(x), fmod(x, y) :
   Valeur absolue et modulo en virgule flottante.
exp(x), log(x), log10(x) :
   Fonctions exponentielles et logarithmiques.

Le fichier d'en-tête cstdlib (porté de stdlib.h de C) fournit une fonction rand(), qui génère un nombre entier pseudo-aléatoire compris entre 0 et RAND_MAX (inclus).

/* Test Random Number Generation */
#include <iostream>
#include <cstdlib>  // pour rand(), srand()
#include <ctime>    // pour time()
using namespace std;

int main() {
   // rand() génère un nombre aléatoire dans [0, RAND_MAX]
   cout << "RAND_MAX est " << RAND_MAX << endl;   // 32767

   // Générer 10 nombres pseudo-aléatoires entre 0 et 99
   //   sans initialiser le générateur.
   // Vous obtiendrez la même séquence chaque fois que vous exécuterez ce programme
   for (int i = 0; i < 10; ++i) {
      cout << rand() % 100 << " ";   // nécessite le fichier d'en-tête <cstdlib>
   }
   cout << endl;

   // Initialiser le générateur de nombres aléatoires avec l'heure actuelle
   srand(time(0));   // nécessite les fichiers d'en-tête <cstdlib> et <ctime>
   // Générer 10 nombres pseudo-aléatoires
   // Vous obtiendrez une séquence différente à chaque exécution,
   //   car l'heure actuelle est différente
   for (int i = 0; i < 10; ++i) {
      cout << rand() % 100 << " ";   // nécessite le fichier d'en-tête <cstdlib>
   }
   cout << endl;
}

Sortie :

RAND_MAX est 2147483647
83 86 77 15 93 35 86 92 49 21
29 0 83 60 22 55 97 80 68 87
image desc
- name: check if keyword exist
  script: |
    #!/bin/bash
    grep -i 'rand' /home/labex/Code/test.cpp
  error: Oops! We find that you didn't use "rand()" method in "test.cpp".
  timeout: 3

Sommaire

Les avantages d'utiliser des fonctions sont les suivants :

  1. Diviser pour régner : construire le programme à partir de pièces ou de composants simples et petits. Modulariser le programme en tâches autonomes.
  2. Éviter de répéter le code : il est facile de copier et de coller, mais difficile de maintenir et de synchroniser toutes les copies.
  3. Réutilisation logicielle : vous pouvez réutiliser les fonctions dans d'autres programmes en les emballant dans des codes bibliothèques.