8.3. Déclaration de classes en C++
Afin de permettre la définition des méthodes qui peuvent être appliquées
aux structures des classes C++, la syntaxe des structures C a été étendue (et simplifiée). Il est
à présent possible de définir complètement des méthodes dans la définition de la structure.
Cependant il est préférable de la reporter et de ne laisser que leur déclaration dans la structure.
En effet, cela accroît la lisibilité et permet de masquer l'implémentation de la classe
à ses utilisateurs en ne leur montrant que sa déclaration dans un fichier d'en-tête. Ils ne peuvent
donc ni la voir, ni la modifier (en revanche, ils peuvent toujours voir la structure de données
utilisée par son implémentation).
La syntaxe est la suivante :
struct Nom
{
[type champs;
[type champs;
[...]]]
[méthode;
[méthode;
[...]]]
};
où
Nom est le nom de la classe. Elle peut contenir divers champs de divers types.
Les méthodes peuvent être des définitions de fonctions, ou seulement leurs déclarations.
Si on ne donne que leurs déclarations, on devra les définir plus loin. Pour cela, il faudra
spécifier la classe à laquelle elles appartiennent avec la syntaxe suivante :
type classe::nom(paramètres)
{
/* Définition de la méthode. */
}
La syntaxe est donc identique à la définition d'une fonction normale,
à la différence près que leur nom est précédé du nom de la classe à laquelle elles appartiennent
et de deux deux-points (::). Cet opérateur
:: est appelé l'opérateur de résolution de portée.
Il permet, d'une manière générale, de spécifier le bloc auquel l'objet qui le suit appartient. Ainsi,
le fait de précéder le nom de la méthode par le nom de la classe permet au compilateur de savoir
de quelle classe cette méthode fait partie. Rien n'interdit, en effet, d'avoir des méthodes
de même signature, pourvu qu'elles soient dans des classes différentes.
Exemple 8-1. Déclaration de méthodes de classe
struct Entier
{
int i; // Donnée membre de type entier.
// Fonction définie à l'intérieur de la classe :
int lit_i(void)
{
return i;
}
// Fonction définie à l'extérieur de la classe :
void ecrit_i(int valeur);
};
void Entier::ecrit_i(int valeur)
{
i=valeur;
return ;
}Note : Si la liste des paramètres de la définition de la fonction contient
des initialisations supplémentaires à celles qui ont été spécifiées dans la déclaration de la fonction,
les deux jeux d'initialisations sont fusionnées et utilisées dans le fichier où la définition
de la fonction est placée. Si les initialisations sont redondantes ou contradictoires, le compilateur
génère une erreur.
Note : L'opérateur de résolution de portée permet aussi de spécifier le bloc
d'instructions d'un objet qui n'appartient à aucune classe. Pour cela, on ne mettra aucun nom avant
l'opérateur de résolution de portée. Ainsi, pour accéder à une fonction globale à l'intérieur
d'une classe contenant une fonction de même signature, on fera précéder le nom de la fonction globale
de cet opérateur.
Exemple 8-2. Opérateur de résolution de portée
int valeur(void) // Fonction globale.
{
return 0;
}
struct A
{
int i;
void fixe(int a)
{
i=a;
return;
}
int valeur(void) // Même signature que la fonction globale.
{
return i;
}
int global_valeur(void)
{
return ::valeur(); // Accède à la fonction globale.
}
}; De même, l'opérateur de résolution de portée permettra d'accéder
à une variable globale lorsqu'une autre variable homonyme aura été définie dans le bloc en cours.
Par exemple :
Les champs d'une classe peuvent être accédés comme des variables normales
dans les méthodes de cette classe.
Exemple 8-3. Utilisation des champs d'une classe dans une de ses méthodes
struct client
{
char Nom[21], Prenom[21]; // Définit le client.
unsigned int Date_Entree; // Date d'entrée du client
// dans la base de données.
int Solde;
bool dans_le_rouge(void)
{
return (Solde<0);
}
bool bon_client(void) // Le bon client est
// un ancien client.
{
return (Date_Entree<1993); // Date limite : 1993.
}
}; Dans cet exemple, le client est défini par certaines données. Plusieurs méthodes
sont définies dans la classe même.
L'instanciation d'un objet se fait comme celle d'une simple variable :
classe objet;
Par exemple, si on a une base de données devant contenir 100 clients,
on peut faire :
On remarquera qu'il est à présent inutile d'utiliser le mot clé
struct pour déclarer une variable, contrairement à ce que la syntaxe du C exigeait.
L'accès aux méthodes de la classe se fait comme pour accéder aux champs
des structures. On donne le nom de l'objet et le nom du champ ou de la méthode, séparés par un point.
Par exemple :
Lorsque les fonctions membres d'une classe sont définies dans la déclaration de cette classe,
le compilateur les implémente en inline (à moins qu'elles ne soient
récursives ou qu'il existe un pointeur sur elles).
Si les méthodes ne sont pas définies dans la classe, la déclaration
de la classe sera mise dans un fichier d'en-tête, et la définition des méthodes sera reportée dans
un fichier C++, qui sera compilé et lié aux autres fichiers utilisant la classe client. Bien entendu,
il est toujours possible de déclarer les fonctions membres comme étant des fonctions
inline même lorsqu'elles sont définies en dehors de la déclaration de la classe.
Pour cela, il faut utiliser le mot clé inline, et placer le code de ces fonctions
dans le fichier d'en-tête ou dans un fichier .inl.
Sans fonctions inline, notre exemple devient :
Fichier client.h :
Fichier client.cc :