Académique Documents
Professionnel Documents
Culture Documents
Laurent SARRY
laurent.sarry@uca.fr
Quizz Wooclap #9
Passage d’arguments
• Rappel : il existe trois types de passages d’arguments
• par valeur : l’argument est copié dans la valeur du paramètre
correspondant de la fonction
• par référence : une référence vers l’argument est passée au
paramètre correspondant de la fonction (pas de copie). Utilisé pour
des paramètres de sortie destinés à être modifiés, ou pour des
paramètres d’entrée si référence constante
• par adresse : l’adresse de la variable en argument est passée à la
fonction plutôt que sa valeur. Le paramètre de la fonction doit donc
être un pointeur
Passage d’arguments
• Bonnes pratiques :
• Réserver le passage par valeur aux arguments en entrée et aux types
simples
• Privilégier le passage par référence pour les arguments en entrée
(const) ou en sortie dont la copie est lourde (structure ou classe)
• Réserver le passage par adresse aux pointeurs et aux tableaux de type
C (convertis en pointeurs)
Passage d’arguments
• Remarque : le passage par adresse permet de modifier la
valeur, mais pas l’adresse pointée. Pour modifier cette
dernière, il faut passer le pointeur par référence
void ValeurASix(int* ptr) int main()
{ {
*ptr = 6; int five{ 5 };
} int* ptr{ &five };
ValeurASix(ptr); // valeur 5 -> 6
void AdresseANull(int*& ptr)
AdresseANull(ptr); // pointeur null
{ return 0;
ptr = nullptr; }
}
Question #1
Question #2
Valeur retournée
• Retour par valeur : une copie est retournée à la fonction
appelante. Convient pour le retour de variables déclarées dans
la fonction ou d’arguments passés par valeur
int doubleValeur(int x)
{
int valeur{ x * 2 };
return valeur; // une copie est retournée
} // valeur détruite à la fin de la fonction
Valeur retournée
• Retour par adresse : une adresse de variable est retournée à la
fonction appelante. Ne convient pas pour une variable locale
dont la portée est limitée, mais pour allocation dynamique
int* AllocationTableau(int taille)
{
return new int[taille];
}
int main()
{
int* tab{ AllocationTableau(5) };
// utilisation du tableau tab
delete[] tab;
return 0;
}
Valeur retournée
• Retour par référence : une référence de variable est retournée
à la fonction appelante. Ne convient pas pour une variable
locale dont la portée est limitée, mais pour un argument
#include <array>
// Retourne une référence sur l'élément index du tableau
int& getElement(std::array<int, 25>& tab, int index)
{
return tab[index]; // tableau non détruit car argument
}
int main()
{
std::array<int, 25> tableau;
getElement(tableau, 10) = 5; // valeur 5 dans l'élément 10
return 0;
}
Valeur retournée
• Retour de plusieurs valeurs : le C++ ne permet pas de
retourner directement plusieurs valeurs
• Utiliser des arguments en sortie (par adresse ou par référence)
• Retourner une structure qui contient plusieurs champs
• Retourner un std::tuple
std::tuple<int, double> retourneTuple() int a2;
{ double b2;
return { 5, 6.7 }; std::tie(a2, b2) = retourneTuple();
} int a3;
int main() double b3;
{ // C++17
std::tuple s{ retourneTuple() }; auto [a3, b3]{ retourneTuple() };
int a1{ std::get<0>(s) }; return 0;
double b1{ std::get<1>(s) }; }
Question #3
Question #4
Question #5
Question #6
Pointeur de fonction
• Comme un pointeur est une variable qui contient l’adresse
d’une autre variable, un pointeur de fonction est une variable
qui contient l’adresse d’une fonction
• Exemple :
• exemple
// test de la parité avec une fonction lambda
return std::all_of(tab.begin(), tab.end(), [](int i){ return ((i % 2) == 0); });
Fonction lambda
• Il existe trois méthodes pour nommer une fonction lambda
• Un pointeur de fonction s’il n’y a pas de clause de capture
double (*addition1)(double, double){
[](double a, double b) {
return (a + b); } };
auto addition{
[](auto a, auto b) {
return (a + b);
}
};
Fonction lambda
• Déduction du type de retour : automatique mais doit être
spécifié en cas d’ambiguïté
#include <iostream>
int main()
{
auto division{ [](int x, int y, bool bInteger) -> double {
if (bInteger)
return x / y;
else
return static_cast<double>(x) / y;
} };
std::cout << division(3, 2, true) << '\n';
std::cout << division(3, 2, false) << '\n';
return 0;
}
Fonction lambda
• Zone de capture : par défaut, une fonction lambda est isolée
du reste du code et n’a pas accès aux variables de la fonction
où elle est définie
• La zone de capture permet de capturer des variables par valeur ou
par référence
• Par défaut, les variables sont capturées par valeur constante. Si on
veut les modifier dans la fonction lambda, il faut utiliser le mot clé
mutable
Fonction lambda
• Zone de capture : capture par valeur constante
int compteur{ };
auto incremente{
[compteur]() {
++compteur; // interdit valeur constante
}
};
incremente();
Fonction lambda
• Zone de capture : capture par valeur
int compteur{ };
auto incremente{
[compteur]() mutable {
++compteur; // modification de la copie locale
}
};
incremente();
Fonction lambda
• Zone de capture : capture par référence
int compteur{ };
auto incremente{
[&compteur]() {
++compteur; // modification de la variable compteur
}
};
incremente();
Fonction lambda
• Zone de capture par défaut : il est possible de capturer toutes
les variables par valeur avec = et par référence avec &
int varInt{ 5 };
double varDouble{ 10.0 };
std::vector<int> vectInt{};