Programmation C-C++/C++ : La couche objet/Données et fonctions membres statiques
Nous allons voir dans ce paragraphe l'emploi du mot clé static dans les classes. Ce mot clé intervient pour caractériser les données membres statiques des classes, les fonctions membres statiques des classes, et les données statiques des fonctions membres.
Données membres statiques
modifierUne classe peut contenir des données membres statiques. Ces données sont soit des données membres propres à la classe, soit des données locales statiques des fonctions membres de la classe. Dans tous les cas, elles appartiennent à la classe, et non pas aux objets de cette classe. Elles sont donc communes à tous ces objets.
Il est impossible d'initialiser les données d'une classe dans le constructeur de la classe, car le constructeur n'initialise que les données des nouveaux objets. Les données statiques ne sont pas spécifiques à un objet particulier et ne peuvent donc pas être initialisées dans le constructeur. En fait, leur initialisation doit se faire lors de leur définition, en dehors de la déclaration de la classe. Pour préciser la classe à laquelle les données ainsi définies appartiennent, on devra utiliser l'opérateur de résolution de portée (::).
Exemple 8-13. Donnée membre statique
modifierclass test
{
static int i; // Déclaration dans la classe.
...
};
int test::i=3; // Initialisation en dehors de la classe.
La variable test::i sera partagée par tous les objets de classe test, et sa valeur initiale est 3.
Les variables statiques des fonctions membres doivent être initialisées à l'intérieur des fonctions membres. Elles appartiennent également à la classe, et non pas aux objets. De plus, leur portée est réduite à celle du bloc dans lequel elles ont été déclarées. Ainsi, le code suivant :
#include <stdio.h>
class test
{
public:
int n(void);
};
int test::n(void)
{
static int compte=0;
return compte++;
}
int main(void)
{
test objet1, objet2;
printf("%d ", objet1.n()); // Affiche 0
printf("%d\n", objet2.n()); // Affiche 1
return 0;
}
affichera 0 et 1, parce que la variable statique compte est la même pour les deux objets.
Fonctions membres statiques
modifierLes classes peuvent également contenir des fonctions membres statiques. Cela peut surprendre à première vue, puisque les fonctions membres appartiennent déjà à la classe, c'est-à-dire à tous les objets. En fait, cela signifie que ces fonctions membres ne recevront pas le pointeur sur l'objet this, comme c'est le cas pour les autres fonctions membres. Par conséquent, elles ne pourront accéder qu'aux données statiques de l'objet.
Exemple 8-14. Fonction membre statique
modifierclass Entier
{
int i;
static int j;
public:
static int get_value(void);
};
int Entier::j=0;
int Entier::get_value(void)
{
j=1; // Légal.
return i; // ERREUR ! get_value ne peut pas accéder à i.
}
La fonction get_value de l'exemple ci-dessus ne peut pas accéder à la donnée membre non statique i, parce qu'elle ne travaille sur aucun objet. Son champ d'action est uniquement la classe Entier. En revanche, elle peut modifier la variable statique j, puisque celle-ci appartient à la classe Entier et non aux objets de cette classe.
L'appel des fonctions membre statiques se fait exactement comme celui des fonctions membres non statiques, en spécifiant l'identificateur d'un des objets de la classe et le nom de la fonction membre, séparés par un point. Cependant, comme les fonctions membres ne travaillent pas sur les objets des classes mais plutôt sur les classes elles-mêmes, la présence de l'objet lors de l'appel est facultatif. On peut donc se contenter d'appeler une fonction statique en qualifiant son nom du nom de la classe à laquelle elle appartient à l'aide de l'opérateur de résolution de portée.
Exemple 8-15. Appel de fonction membre statique
modifierclass Entier
{
static int i;
public:
static int get_value(void);
};
int Entier::i=3;
int Entier::get_value(void)
{
return i;
}
int main(void)
{
// Appelle la fonction statique get_value :
int resultat=Entier::get_value();
return 0;
}
Les fonctions membres statiques sont souvent utilisées afin de regrouper un certain nombre de fonctionnalités en rapport avec leur classe. Ainsi, elles sont facilement localisable et les risques de conflits de noms entre deux fonctions membres homonymes sont réduits. Nous verrons également dans le Chapitre 11 comment éviter les conflits de noms globaux dans le cadre des espaces de nommage.