Pointeurs de fonction et callbacks en C et C++
Dans cette leçon, vous allez passer une fonction en paramètre à une autre fonction, avec des pointeurs de fonction en C et des lambdas en C++.
Introduction
Un callback consiste à passer une fonction à une autre fonction, qui la rappellera quand elle en aura besoin. Un algorithme générique peut ainsi exécuter le comportement choisi par l'appelant sans connaître les détails de son implémentation. Par exemple, la même fonction apply peut mettre au carré, doubler ou appliquer n'importe quelle autre transformation.
Points importants
- Un pointeur ne doit pointer que vers un objet qui existe encore, du bon type, ou valoir null.
- Chaque zone mémoire allouée doit être libérée exactement une fois.
- Indiquez clairement quelle fonction possède la mémoire (ownership) et quelles données sont seulement lues (const).
Exemples de code C et C++
#include <stdio.h>
int square(int value) { return value * value; }
int apply(int value, int (*operation)(int)) { return operation(value); }
int main(void) {
printf("%d\n", apply(6, square));
return 0;
}
36
#include <functional>
#include <iostream>
int apply(int value, const std::function<int(int)>& operation) {
return operation(value);
}
int main() {
int factor = 6;
std::cout << apply(factor, [](int value) { return value * value; }) << '\n';
}
36
Comparaison entre C et C++
En C, le paramètre int (*operation)(int) est un pointeur de fonction qui accepte une fonction prenant un int et renvoyant un int, donc apply(6, square) renvoie 36. En C++, les lambdas et std::function peuvent contenir une plus grande variété d'objets appelables (callables). Pour du code où les performances comptent, un template est souvent un meilleur choix que std::function.
La possession et la libération de la mémoire reposent entièrement sur le programmeur.
Les conteneurs et RAII libèrent automatiquement les ressources quand l'objet arrive en fin de vie.
Exercices d'approfondissement
Exécutez les deux versions, puis modifiez le code pour observer la différence de sécurité qu'apporte chaque langage.
- Testez avec des pointeurs nuls et des échecs d'allocation mémoire.
- Exécutez le programme avec AddressSanitizer et UndefinedBehaviorSanitizer pour détecter les erreurs mémoire.
- Indiquez clairement quel pointeur possède la mémoire et lequel sert seulement à lire.
S'entraîner avec un défi
Utilisez le contenu de cette leçon pour résoudre un vrai problème. Chaque défi s'exécute dans le navigateur, avec des tests instantanés et une revue par l'IA.
- Fonctions d'activation Appliquez sigmoïde, ReLU, Leaky ReLU ou tanh à chaque valeur d'une liste. Facile