Pointeurs, références & allocation dynamique Rappels sur les fonctions Les spécificités du C++ Structures et classes Encapsulation des données Notions de constructeur et de destructeur Fonctions et classes amies Surcharge d'opérateur Héritage Notions de patrons de fonctions et de classes Introduction à la librairie standard STL
Compilation et directives de préprocesseur Convention d'écriture et organisation des programmes Écriture/lecture sur l'entrée/sortie standard Les membres données statiques Utilisation de enum et de typedef
Retour menu principal

Structures et classes


Ce chapitre aborde véritablement les possibilités offertes par la programmation orientée objet grâce à l’implé­men­tation des classes. Une classe est la généralisation de la notion de type défini par l’utilisateur, dans lequel se trouvent associées aussi bien des données (membres données) que des méthodes (fonctions membres). En règle générale, il existe une nette distinction entre les données d’un programme et le code exécutable des fonctions de ce programme. En effet, les données et le code ne se mélangent généralement pas dans la mémoire de l’ordinateur (sauf cas très particuliers tels que l’autoprogrammation, les virus ou encore les débogueurs). Les langages objet permettent, par l’intermédiaire de constructions syntaxiques relativement simples, de définir de manière groupée les données à manipuler et les opérations qu’on peut leur appliquer. Dans ce cadre, un objet n’est rien d’autre que des données qui le caractérisent, sur lesquelles des traitements spécifiques peuvent être appliqués.

Par ailleurs, la programmation orientée objet ne se conçoit que dans le contexte d’une analyse du problème à traiter plus “orientée” sur la nature et la structure des données manipulées (autrement dit, les objets) et des opérations associées que sur la séquence des traitements que le programme doit effectuer. Elle se distingue en cela de la programmation impérative (ou procédurale) classique telle qu’utilisée en C ou fortran, qui s’intéresse plus à l’algorithmique qu’aux structures de données.

En pratique, il est courant que plusieurs objets aient la même structure de données et puissent subir les même traitements. On considère dans ce cas que ces objets sont de même nature, et sont en réalité des instances d’une même classe d’objets. La classe d’un objet apparaît donc réellement comme son type de données, définissant sa structure et les opérations qui peuvent lui être appliquées.

Dans ce premier chapitre consacré à la programmation orientée objet, nous présenterons la version étendue du type structure introduite par le C++. Ce nouveau type permettra, dans un premier temps, de nous limiter à la façon de mettre en œuvre l’association des données et des méthodes et facilitera l’introduction à la notion même de classe. Ce n’est qu’au travers du prochain chapitre "Encapsulation des données" qu’apparaîtont les différences conceptuelles entre les types structure et classe.

Les structures en C++

Rappel : les structures en C

En C, une déclaration telle que

struct point
{
  int m_X;
  int m_Y;
};

définit un type structure nommé point dont m_X et m_Y sont des membres. On notera, par ailleurs, la présence d’un point virgule à la fin de la déclaration. L’instanciation c’est-à-dire la déclaration d’objet de type point se fait via des instructions telles que

struct point my_point1, my_point2;

L’accès aux membres de my_point1 et my_point2 se fait à l’aide de l’opérateur point (.); par exemple, my_point1.y désigne le membre y de la structure my_point1.

Déclaration d’une structure comportant des méthodes ou fonctions membres

En C++, il est possible au sein même d’une structure, d’associer aux données i.e. aux membres, des méthodes également appelées “fonction membres”1. Supposons que nous souhaitions associer à la structure point précédente, trois fonctions:

  • initialise pour attribuer des valeurs aux “coordonnées” d’un point,
  • deplace pour modifier les coordonnées d’un point,
  • affiche pour afficher les coordonnées d’un point.

De telles méthodes seraient ainsi déclarées

struct point
{
  // Déclaration des membres
  int m_X;
  int m_Y;
  // Déclaration des méthodes ou fonctions membres
  void initialise(const int abscisse, const int ordonnee);
  void deplace(const int new_abscisse, const int new_ordonnee);
  void affiche();
};

Outre la déclaration classique des membres de la structure apparaissent les déclarations ou prototypes des trois fonctions. Notez bien que la définition de ces méthodes ne figure pas à ce niveau de simple déclaration. En revanche, le type d’arguments à fournir aux différentes méthodes est précisé2 bien que l’usage de ces variables soit à ce stade de la déclaration inconnu.

Définition des méthodes

La définition des méthodes se fait comme une fonction à ceci près que le nom de la structure doit être précisé afin d’évaluer “la portée” de cette fonction. À titre d’exemple, la méthode initialise pourra être définie ainsi

void point::initialise(const int abscisse, const int ordonnee)
{
  m_X = abscisse;
  m_Y = ordonnee;
}

Dans l’en-tête, le nom de la méthode est point::initialise où le symbole :: correspond à l’opérateur de résolution de portée. Ce dernier sert à modifier la portée d’un identificateur; dans le cas présent, il signifie que l’identificateur initialise concerné relève de la structure point.

Dans le détail de la définition, deux affectations sont réalisées: celle de la valeur abscisse, respectivement ordonnee, vers la variable m_X, respectivement m_Y. Le variable m_X n’est ni un argument ni une variable locale à la méthode; m_x désigne le membre m_X correspondant au type point. Il est important de noter qu’une telle association entre méthode et données membres d’une structure n’est réalisée qu’au travers du préfixe point::. En l’absence de ce “préfixe”, nous définirions effectivement une fonction nommée initialise, indépendante de la structure point, et ne pouvant accéder et donc modifier les membres m_X et m_Y.

En outre, la définition de la méthode initialise et de ces consœurs deplace et affiche, ne peut être compilée seules. Elle nécessite l’inclusion des instructions de déclaration correspondantes et présentées plus tôt dans le paragraphe. Dans la pratique, la déclaration de la structure trouvera naturellement sa place au sein d’un fichier d’en-tête dûment nommé (point.h, par exemple) tandis que la définition des méthodes s’insèrera dans un fichier source (point.cc, par exemple). L’inclusion du fichier d’en-tête au sein du fichier source est alors impérative.

Utilisation d’une structure comportant des méthodes

Disposant à présent du type point tel que déclaré au paragraphe Déclaration d’une structure comportant des méthodes ou fonctions membres et défini au paragraphe Définition des méthodes, nous pouvons déclarer autant d’objets i.e. de structures de type point. L’instruction suivante

point my_point1, my_point2;

déclare deux structures nommées my_point1 et my_point2, chacune disposant de trois méthodes initialise, deplace et affiche. L’accès aux membres m_X et m_Y peut se faire comme en C à savoir my_point1.m_X = 5;. Cependant et comme nous le verrons au prochain chapitre "Encapsulation des données", nous briserions alors le principe d’encapsulation à savoir que nous accèderions directement aux données membres sans passer par l’intermédiaire des méthodes. Aussi, on priviligiera la manipulation des membres via les méthodes dédiées soit

mypoint1.initialise(5, 2);

Une telle instruction consiste à appeler la méthode Initialise pour la structure my_point1 en lui transmettant en arguments les valeurs 5 et 2. Abstraction faite du préfixe my_point1., cet appel est analogue à un appel classique de fonction. Aussi, l’ajout du préfixe my_point1. précise à la méthode quelle est la structure sur laquelle opérer.

Remarques

  • Une méthode ne peut être appelée comme une fonction ordinaire. Par exemple, l’instruction suivante

    initialise(5, 2);
    

    sera rejetée à la compilation (à moins qu’il n’existe, par ailleurs, une fonction ordinaire appelée initialise).

  • Dans la déclaration d’une structure, il est permis (mais généralement peu conseillé) d’introduire les membres et les méthodes dans un ordre quelconque.
  • Un exemple de programme complet comprendrait

    • la déclaration du type point,
    • la définition des méthodes du type point,
    • un programme principal contenant la fonction main et utilisant le type point.

    La compilation séparée prend alors tout son intérêt puisqu’il est possible de compiler le type point indépendamment de son utilisation faite dans le programme principal; c’est ainsi que l’on pourra “réutiliser” un composant logiciel.

Notion de classe

En C++, la structure est un cas particulier de la classe. Comme nous le verrons au prochain chapitre, une classe est une structure dans laquelle seulement certains membres et/ou méthodes sont accessibles “de l’extérieur”: on parlera alors de membres/méthodes “publics” par opposition aux membres/méthodes dites “privés”. Toutefois, dans le cadre de ce chapitre d’introduction aux notions de structure/classe, la déclaration d’une classe est voisine de celle d’une structure à ceci près que le mot clé struct est remplacé par le terme class. Par ailleurs, pour avoir un comportement identique à celui d’une structure, il convient d’ajouter le mot clé public afin que membres et méthodes soient accessibles et manipulables depuis l’extérieur de la classe. Par défaut, les membres et méthodes d’une classe sont privés (mot clé private) alors que ceux d’une structure sont publics. L’ensemble de ces concepts est précisé dans le chapitre "Encapsulation des données".

Notes :

1

d’un point de vue conceptuel, une telle notion ne prendra tout son intérêt qu’une fois l’encapsulation des données introduite. Cependant, la présentation du concept de méthode prépare à la notion de classe.

2

l’ajout des noms des variables est facultative. Toutefois, elle facilite la lecture et la compréhension du code, a fortiori pour l’utilisateur qui n’a, en théorie, accès qu’à la déclaration des méthodes et non à leur définition.