8.16. Pointeurs sur les membres d'une classe

Nous avons déjà vu les pointeurs sur les objets. Il nous reste à voir les pointeurs sur les membres des classes.

Les classes regroupent les caractéristiques des données et des fonctions des objets. Les membres des classes ne peuvent donc pas être manipulés sans passer par la classe à laquelle ils appartiennent. Par conséquent, il faut, lorsqu'on veut faire un pointeur sur un membre, indiquer le nom de sa classe. Pour cela, la syntaxe suivante est utilisée :

définition classe::* pointeur

Par exemple, si une classe test contient des entiers, le type de pointeurs à utiliser pour stocker leur adresse est :

int test::*

Si on veut déclarer un pointeur p de ce type, on écrira donc :

int test::*p1;   // Construit le pointeur sur entier
                 // de la classe test.

Une fois le pointeur déclaré, on pourra l'initialiser en prenant l'adresse du membre de la classe du type correspondant. Pour cela, il faudra encore spécifier le nom de la classe avec l'opérateur de résolution de portée :

p1 = &test::i;   // Récupère l'adresse de i.

La même syntaxe est utilisable pour les fonctions. L'emploi d'un typedef est dans ce cas fortement recommandé. Par exemple, si la classe test dispose d'une fonction membre appelée lit, qui n'attend aucun paramètre et qui renvoie un entier, on pourra récupérer son adresse ainsi :

typedef int (test::* pf)(void);  // Définit le type de pointeur.
pf p2=&test::lit;                // Construit le pointeur et
                                 // lit l'adresse de la fonction.

Cependant, ces pointeurs ne sont pas utilisables directement. En effet, les données d'une classe sont instanciées pour chaque objet, et les fonctions membres reçoivent systématiquement le pointeur this sur l'objet de manière implicite. On ne peut donc pas faire un déréférencement direct de ces pointeurs. Il faut spécifier l'objet pour lequel le pointeur va être utilisé. Cela se fait avec la syntaxe suivante :

objet.*pointeur

Pour les pointeurs d'objet, on pourra utiliser l'opérateur ->* à la place de l'opérateur .* (appelé pointeur sur opérateur de sélection de membre).

Ainsi, si a est un objet de classe test, on pourra accéder à la donnée i de cet objet à travers le pointeur p1 avec la syntaxe suivante :

a.*p1 = 3;  // Initialise la donnée membre i de a avec la valeur 3.

Pour les fonctions membres, on mettra des parenthèses à cause des priorités des opérateurs :

int i = (a.*p2)();   // Appelle la fonction lit() pour l'objet a.

Pour les données et les fonctions membres statiques, cependant, la syntaxe est différente. En effet, les données n'appartiennent plus aux objets de la classe, mais à la classe elle-même, et il n'est plus nécessaire de connaître l'objet auquel le pointeur s'applique pour les utiliser. De même, les fonctions membres statiques ne reçoivent pas le pointeur sur l'objet, et on peut donc les appeler sans référencer ce dernier.

La syntaxe s'en trouve donc modifiée. Les pointeurs sur les membres statiques des classes sont compatibles avec les pointeurs sur les objets et les fonctions non-membres. Par conséquent, si une classe contient une donnée statique entière, on pourra récupérer son adresse directement et la mettre dans un pointeur d'entier :

int *p3 = &test::entier_statique;   // Récupère l'adresse
                                    // de la donnée membre
                                    // statique.

La même syntaxe s'appliquera pour les fonctions :

typedef int (*pg)(void);
pg p4 = &test::fonction_statique;   // Récupère l'adresse
                                    // d'une fonction membre
                                    // statique.

Enfin, l'utilisation des ces pointeurs est identique à celle des pointeurs classiques, puisqu'il n'est pas nécessaire de fournir le pointeur this. Il est donc impossible de spécifier le pointeur sur l'objet sur lequel la fonction doit travailler aux fonctions membres statiques. Cela est naturel, puisque les fonctions membres statiques ne peuvent pas accéder aux données non statiques d'une classe.

Exemple 8-27. Pointeurs sur membres statiques

#include <iostream>

using namespace std;

class test
{
    int i;
    static int j;

public:
    test(int j)
    {
        i=j;
        return ;
    }

    static int get(void)
    {
        /* return i ;  INTERDIT : i est non statique
                       et get l'est ! n */
        return j;	// Autorisé.
    }
};

int test::j=5;             // Initialise la variable statique.

typedef int (*pf)(void);   // Pointeur de fonction renvoyant
                           // un entier.
pf p=&test::get;           // Initialisation licite, car get
                           // est statique.

int main(void)
{
    cout << (*p)() << endl;// Affiche 5. On ne spécifie pas l'objet.
    return 0;
}