[30] Pointeurs de fonctions membres

(Une partie de C++ FAQ Lite fr, Copyright © 1991-2002, Marshall Cline, cline@parashift.com)

Traduit de l'anglais par Philippe Elie

Les FAQs de la section [30]


[30.1] Le type des "pointeurs de fonction" membre est-il différent du type des "pointeurs de fonctions"?
[Recently changed "It's type is..." to "Its type is..." thanks to Graham Borland (on 1/00). ]

Oui.

Considérez la fonction suivante:

int f(char a, float b);

Le type de cette fonction est différent s'il s'agit d'une fonction ordinaire ou d'un fonction membre non statique d'une classe classe:

Note: Si la fonction est une fonction membre statiqued'une classeFred, son type est le même que celui d'une fonction ordinaire: "int (*)(char,float)".

[ Haut | Bas | Rechercher ]


[30.2] Comment passer un pointeur de fonction membre a un gestionnaire de signal, a une fonction de rappel d'un événement X, etc.?
[Récemment ajouté type de retour de main main() (on 10/99).]

Ne le faites pas.

Parce qu'un pointeur de fonction membre est sans signification sans un objet sur laquelle l'invoquer, vous ne pouvez pas faire cela directement (Si X Windows était réécrit en C++, il passerai probablement des références vers des objets, et non pas seulement des pointeurs de fonctions; naturellement ces objets devraient incorporer la fonction requise et probablement bien d'autre chose).

Comme pour les patchs pour des logiciels existant, utilisé une fonction globale (non membre) comme enveloppe qui prend un objet obtenu par une autre technique (dans une variable globale par exemple). Le fonction globale appliquera le pointeur de fonction membre a cette objet.

E.g., supposons que vous voulez appeler Fred::memberFunction() sur un interruption:

class Fred {
public:
void memberFunction();
statique void staticMemberFunction(); // un fonction membre static peut la gérer
// ...
};

// La fonction enveloppe utilise une variable comme objet:
Fred* object_which_will_handle_signal;
void Fred_memberFunction_wrapper()
{
object_which_will_handle_signal->memberFunction();
}

int main()
{
/* signal(SIGINT, Fred::memberFunction); */ // Ne fonctionne pas
signal(SIGINT, Fred_memberFunction_wrapper); // OK
signal(SIGINT, Fred::staticMemberFunction); // Aussi OK
}

Note: Les pointeurs de fonctions membres static ne nécessitent pas d'objets pour les invoquer, aussi les pointeurs de fonctions membres static on un type compatible avec les pointeurs de fonctions.

[ Haut | Bas | Rechercher ]


[30.3] Pourquoi ai-je des erreurs à la compilation (incohérence de type) quand j'éssaie d'utiliser un pointeur de fonction membre comme fonction de gestion d'une interruption?
Ceci est un cas spécial des deux FAQ précédentes, aussi lisez les deux réponses d'abord.

Les fonctions membres non statiques ont un paramètre caché qui correspond au pointeur this. Le pointeur this pointe vers l'instance de l'objet implicite. Le gestionnaire matériel/logiciel d'interruption n'est pas capable de fournir l'argument pointeur this a la routine. Vous devez utiliser une fonction "normal" (fonction non membre) ou une fonction membre statique comme gestionnaire d'interruption.

Une solution possible est d'utiliser une fonction membre static membre comme gestionnaire d'interruption, cette routine devra connaître la pair objet/pointeur de fonction membre qui doit être appelé lors de l'interruption. Ainsi l'effet est qu'une fonction membre est invoqué par interruption mais que pour des raisons techniques vous avez besoin de l'appel a une fonction intermédiaire en premier lieu.

[ Haut | Bas | Rechercher ]


[30.4] Pourquoi ai-je des problèmes quand j'utilise l'adresse d'une fonction C++?
Ceci est un corollaire de la FAQ précédente.

Réponse détaillée: En C++, les fonctions membres possèdent un paramètre supplémentaire implicite qui pointe vers l'objet (le pointeur this dans les fonctions membres). Les fonctions C normales peuvent être vu comme ayant une convention d'appel différentes des fonctions membres, aussi leurs types (pointeur de fonction membre contre pointeur de fonction) sont différents et incompatibles. Le C++ introduit un nouveau type de pointeur appelé pointeur de membre, qui peut être utilisé seulement en fournissant un objet.

NOTE: N'essayez pas de tenter de "transtyper" un pointeur de fonction membre vers un pointeur de fonction; le résultat est indéfini et probablement désastreux. E.g., un pointeur de fonction membre n'est pas requis de contenir l'adresse machine de la fonction appropriée. Comme nous l'avons dit dans le dernier exemple, si vous avez un pointeur de fonction C normal, utilisez soit une fonction non membre soit une fonction membre static (de classe).

[ Haut | Bas | Rechercher ]


[30.5] Comment éviter des erreurs de syntaxes quand j'appelle une fonction en utilisant un pointeur de fonction membre?
Deux choses: (1) utilisez un typedef, et (2) utilisez une macro #define.

Voici comment créer le typedef:

class Fred {
public:
int f(char x, float y);
int g(char x, float y);
int h(char x, float y);
int i(char x, float y);
// ...
};

// FredMemberFn pointe vers un membre de Fred qui prend (char,float)
typedef int (Fred::*FredMemberFn)(char, float);

Voici comment créer la macro #define (normalement je n'aime pas les macros #define , mais ceci est un des rares cas ou une macro facilitera l'écriture et la lisibilité de votre code):

#define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember))

Voici un exemple d'utilisation:

void userCode(Fred& fred, FredMemberFn memFn)
{
callMemberFunction(fred,memFn)('x', 3.14);
// devrait normalement être: (fred.*memFn)('x', 3.14);
}

Je recommande fortement l'utilisation de cette méthode. Dans le monde réel l'utilisation des pointeurs de fonction membre est beaucoup plus complexe que dans l'exemple donné, et la différence en terme de facilité d'écriture et de lisibilité est significative. comp.lang.c++ a enduré des centaines et des centaines de messages de programmeurs ayant eu des problèmes de syntaxe avec des pointeurs de fonctions membres. La plupart de ces erreurs ne se serait pas produite en utilisant la méthode ci-dessus.

[ Haut | Bas | Rechercher ]


[30.6] Comment créer et utiliser des tableaux de pointeurs de fonctions membres?
Utilisez la méthode typedef et macro #define et vous en aurez fait 90%.

D'abord écrivez un typedef:

class Fred {
public:
int f(char x, float y);
int g(char x, float y);
int h(char x, float y);
int i(char x, float y);
// ...
};

// FredMemberFn pointe vers un membre de Fred qui prend (char,float)
typedef int (Fred::*FredMemberFn)(char, float);

Ce qui permet d'écrire le tableau de pointeur de fonction membre directement:

FredMemberFn a[4] = { &Fred::f, &Fred::g, &Fred::h, &Fred::i };

Ensuite, définissez la macro callMemberFunction:

#define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember))

Ce qui permet d'appeler une des fonctions membres sur l'objet "fred" directement:

void userCode(Fred& fred, int memberFunctionNum)
{
// pré-supose que memberFunctionNum est compris entre 0 and 3 inclus:
callMemberFunction(fred, a[memberFunctionNum]) ('x', 3.14);
}

[ Haut | Bas | Rechercher ]


E-mail Marshall Cline Ecrire à l'auteur, au traducteur, ou en savoir plus sur la traduction.
C++ FAQ Lite fr | Table des matières | Index | A propos de l'auteur | © | Téléchargez votre propre copie ]
Dernière révision Sun Apr 13 23:54:31 PDT 2003