Structures et classes
Ce chapitre aborde véritablement les possibilités offertes par la programmation orientée objet grâce à l’implémentation 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 typepoint
.
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.- la déclaration du type
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 :
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.
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.