Dès qu’en C++ je dois faire des pointeurs sur fonction membre (ou méthode) d’une classe c’est toujours la même chose : je ne me souviens jamais de l’enfer syntaxique que constitue la déclaration, l’assignation, et l’utilisation d’un pointeur sur fonction membre ! >:(
Combien de parenthèses ? Où ? Quand ? Comment ? Et en plus le nombre / l’emplacement des parenthèses change entre la déclaration et l’appel…
Bref c’est pour ça que je m’écris, ici, définitivement, et aussi pour quiconque à qui ça pourrait servir, ce mémo :
-
Déclaration.
Un pointeur sur fonction normal se déclarerait de la façon suivante :
1 |
int (*fptr)(bool, int) |
fptr est ici un pointeur sur une fonction prenant en paramètre un booléen et un entier, et renvoyant un entier. En C++, le fait qu’une fonction appartient à une classe se décrit dans le code avec l’opérateur d’appartenance « :: », précédé du nom de ladite classe. La méthode « leDauphin » de la classe Echo s’écrirait donc Echo::leDauphin(…). Ainsi, pour déclarer un pointeur sur une fonction membre de la classe Echo, qui retourne un int et prend en paramètre un booléen et un int, on écrira par exemple :
1 |
int (Echo::*fptr)(bool, int) |
Bien pratique quand on a des fonctions d’analyse, ou des setters, qui marchent à peu près pareil, et qui permettent de factoriser du code de façon fabuleuse (comme je le fais actuellement, mais c’est une autre histoire).
-
Assignation.
C’est là que le fun commence : en général un pointeur sur fonction c’est plus utile quand il pointe vers une fonction. Et là, pour setter la valeur du pointeur, attention les yeux : PLUS DE PARENTHÈSES !
1 |
int (Echo::*fptr)(bool, int) = &Echo::leDauphin; |
-
Utilisation.
Un pointeur sur méthode, ou fonction membre, comme son nom l’indique, permet de lancer une fonction faisant partie d’un objet. Il ne rime donc à rien de vouloir l’appeler sans un objet du type correspondant. Et c’est là le summum, le top, le nec plus ultra, car on va mélanger plein de symboles syntaxiques : le point, la parenthèse, l’étoile, bref autant de trucs qui font qu’on a vite fait de se tromper… En bref, l’appel à un pointeur sur fonction membre ça donne ça :
1 |
(echo.*fptr)(true, 42); |
avec echo une classe de type Echo et fptr le pointeur sur fonction que nous avons tous deux évoqués plus haut… Dans le cas d’un pointeur sur classe (alloué avec new par exemple), on remplace juste le . par une flèche ->.
Et voilà, j’ai fini mon mémo !
Et pour finir un protip maison que je tiens déjà de l’époque où je faisais des pointeurs sur fonction en C : mettre la déclaration du type pointeur sur fonction dans un typedef pour avoir un type un peu plus facile à manipuler. Par exemple : au lieu d’écrire:
1 |
int (*totoPtr)(bool, int) |
on pourrait tout simplement faire un :
1 |
typedef int (*fptr)(bool, int); |
et ensuite utiliser le type fptr :
1 |
fptr totoPtr; |
ce qui est absolument équivalent à ce que j’ai écrit juste avant. Ça marche aussi pour les pointeurs sur fonction membre !
Allez… à plus tard les dauphins.
Voir aussi :
Section de la C++ FAQ sur les pointers to member functions (anglais)
Mes récentes mésaventures avec les foncteurs templates (un peu d’auto-pub n’a jamais tué personne)