c++ - l'orienté objet

Décembre 2016

Styles de programmation supportés

Le C++ est un langage de programmation polyvalent qui supporte non seulement la programmation orientée objet, mais aussi l'abstraction des données et la programmation générique en complément des techniques de programmation C traditionnelles.

Le terme « supporte » est important. La programmation orientée objet est une technique de programmation, le langage fournit simplement les mécanismes qui permettront d'appliquer ce style de programmation de façon simple, sécurisée et efficace. Vous pourriez très bien écrire un programme orienté objet en langage C ou un programme structuré en Fortran, mais ces langages ne supportant pas directement ces techniques, cet exercice exigerait des efforts considérables ou un niveau de connaissance très élevé.

Les fonctions du langage fournissent une partie du support de la programmation orientée objet, le reste étant assuré par les contrôles d'exécution et de compilation qui vont détecter toute opération non conforme à ce type de programmation. Le contrôle des types, particulièrement efficace en C++, en est l'exemple le plus frappant.

Comme nous l'avions déjà annoncé en introduction, nous ne détaillerons pas, dans cet ouvrage, les techniques de programmation C traditionnelles. La programmation orientée objet est un sujet complexe et étendu, il serait dommage de s'écarter trop souvent du sujet pour expliquer les techniques de bouclage, énumérer les opérateurs ou expliquer la notion de portée d'une variable.

Concepts clés de l'orienté objet

L'objectif de la programmation est de fournir une solution à un problème, et il existe de multiples façons de parvenir à ce résultat. Dans le cas de la programmation orientée objet, vous n'allez pas raisonner en termes de données et de fonctions pour les manipuler, mais uniquement en termes d'objets. Les objets sont des entités avec des propriétés et des caractéristiques bien spécifiques. Nous y reviendrons un peu plus loin. Si votre programme doit gérer des personnes, vous devrez créer des objets représentant chacune de ces personnes. Le choix des objets s'effectue au cours de l'étape d'analyse qui doit précéder toute programmation. Cette analyse a pour but de bien cerner les différents aspects du problème et de produire un programme plus fiable et plus facile à maintenir. N'oubliez jamais que le coût d'un programme repose à 90 % sur la mise au point et la maintenance. Si vous programmez uniquement pour votre plaisir, ce coût sera l'énergie que vous devrez fournir pour que votre programme fonctionne correctement, alors pensez-y ! Cette analyse présente peu d'intérêt pour développer des programmes courts comme ceux que nous allons étudier, mais elle est primordiale dans le cadre de projets complexes et importants.

Modèle

À la fin de l'analyse, vous devez produire un modèle de l'environnement du problème à résoudre. Ce modèle doit être plus simple que l'environnement réel, mais suffisamment précis pour que, par son intermédiaire, vous puissiez prévoir le comportement des choses réelles.

Objets : "objet" , et classes d'objets : "classe"

Pas de panique, ce n'est pas aussi compliqué que cela en a l'air ! Prenons un exemple. Si notre problème concerne la gestion d'un zoo, nous devons modéliser ce zoo en créant un objet pour chacun de ses éléments. Les objets sont regroupés en catégories appelées classes. Vous allez donc facilement obtenir les classes Singe, Lion, Oiseau, Reptile, etc. Mais si votre programme concerne l'alimentation des animaux, il pourrait être utile de les regrouper dans les classes Carnivore, Végétarien, Insectivore, etc. Cette analyse se traduit par un schéma hiérarchique représentant les classes de la plus générale à la plus « spécialisée » (voir fig. 1.1). Cette décomposition peut se faire à l'infini, jusqu'à ce que vous obteniez une représentation acceptable du monde réel. On dit alors que les classes plus spécialisées dérivent de la classe générale située au niveau supérieur. En effet, elles reprennent certaines particularités de la classe dont elles sont dérivées (nommée classe mère). Ce lien qui lie la fille à sa mère se nomme héritage : "héritage". Quand toutes les classes ont été définies, au dernier niveau de l'arborescence des classes, vous créez les objets Gorille ou Lion spécifiques pour représenter chaque élément du problème (dans notre cas, tous les pensionnaires du zoo). Ces objets sont nommés : "instance de classe" instances de classe. Toute classe peut ainsi avoir un ou plusieurs « représentants » qui possèdent toutes les caractéristiques de la classe dont ils sont issus.

Figure 1.1 : voici la représentation d'une partie de notre modèle de classes. Les classes Gorille, Chimpanzé et Macaque sont dérivées de la classe Singe, elle-même dérivée de la classe Végétarien, cette dernière étant dérivée de la classe de base Animal.

Membres de classe : "membre de classe"

Une fois qu'ils ont été identifiés, les objets ne seraient pas d'une grande utilité s'ils ne possédaient pas des caractéristiques (bruyant, paresseux, agressif, etc.) et un comportement (mord, aboie, chante, vole, etc.).

Les caractéristiques sont enregistrées sous la forme de membres de classe (données membres : "donnée membre"ou variables membres). Elles permettent d'affecter des valeurs à nos objets pour mieux les définir. Pour chacun de nos animaux, nous pouvons créer les données membres : nom, age, couleur, taille. Pour certains d'entre eux nous pourrions également définir les caractéristiques longueur_queue, taille_bec, nombre_mammelles, etc.

Méthodes : "méthode de classe"

Le comportement (les actions que l'objet peut réaliser) s'obtient en définissant des méthodes ou des fonctions membres : "fonction:membre de classe". Ces dernières vont renvoyer des valeurs ou modifier l'environnement. La première catégorie de méthode est celle des accesseurs. Elles renvoient la valeur d'une des variables d'un objet. Nous pourrions ajouter pour chaque objet (animal) un accesseur LireAge() qui renverrait l'age d'un objet Animal permettant à un objet extérieur d'accéder à cette valeur. La seconde catégorie est celle des mutateurs. Ce sont des méthodes qui permettent de changer la variable d'un objet. De nouveau, pour l'âge, nous pourrions créer la méthode DefinirAge() qui permettrait à un objet extérieur de modifier l'âge d'un animal.

Le modèle présenté à la figure 1.2 commence maintenant à s'étoffer. Les méthodes se distinguent des membres de classe par la paire de parenthèses qui suit leur nom.

Figure 1.2 : ce sous-ensemble du modèle présente les données membres et les méthodes définies pour les objets des classes dérivées de Singe.

Héritage : "héritage"

Le modèle commence à s'étoffer mais aussi à se compliquer avec de nombreuses redondances. Nous disposons heureusement du mécanisme de l'héritage, un des concepts les plus importants de l'orienté objet. Par ce mécanisme, toute l'arborescence des sous-classes d'une classe racine hérite des membres et des méthodes de la classe mère. Tous les animaux possédant les caractéristiques nom, age, couleur, taille, par exemple, il serait plus judicieux de les définir une seule fois au niveau de la classe Animal. Les objets des classes dérivées de cette dernière vont alors hériter de ces caractéristiques sans que vous ayez besoin de les redéfinir. L'efficacité du modèle dépend de l'utilisation plus ou moins judicieuse de l'héritage. Quand vous définissez une caractéristique, demandez-vous toujours si cette dernière est partagée par d'autres catégories d'objets, c'est-à-dire par d'autres classes, et définissez-la au niveau de la classe mère appropriée. Les mêmes règles s'appliquent au comportement. Au même titre que les caractéristiques, les méthodes d'une classe seront disponibles dans les sous-classes par le mécanisme de l'héritage. La figure 1.3 représente une branche de notre modèle beaucoup mieux organisé que le précédent.

Figure 1.3 : détail de l'héritage entre la classe Animal et la classe Lion.

L'héritage présente deux avantages importants. Il permet de spécialiser les classes existantes en écrivant uniquement le code modifié. Un Carnivore, par exemple, est un Animal et il possède toutes les caractéristiques de cette classe. Ces dernières vont être héritées, il n'est donc pas besoin de les redéfinir. Un carnivore est un animal qui se nourrit de viande, nous pouvons donc définir le membre Proies bien spécifique à cette catégorie d'animaux. Il n'est plus nécessaire de copier et de coller le code, opération qui pose toujours des problèmes en cas de modification puisqu'il faudra retrouver toutes les occurrences d'un bloc de code donné. Grâce à l'héritage, une modification effectuée dans une classe mère est automatiquement répercutée au niveau de ses classes dérivées.

L'autre avantage de l'héritage est la possibilité de traiter les objets dérivés de façon polymorphique. Poly signifie plusieurs et morphe signifie forme. Le "polymorphisme", autre concept très important de l'orienté objet, est donc la capacité à prendre plusieurs formes. Ce mécanisme complexe est détaillé à partir du chapitre 6. Retenez simplement que vous pouvez redéfinir dans des classes dérivées une fonction déjà définie dans une classe de base. Dans notre exemple, nous pourrions redéfinir la fonction générale Manger() dans les classes Végétarien et Carnivore pour l'adapter à ces deux catégories d'animaux, et même la redéfinir encore au niveau d'une classe plus spécifique pour se rapprocher du comportement réel de l'animal. Cela permet d'écrire du code générique comme :

p_animal.Manger();

p_animal étant un pointeur, cette instruction se traduit par « appeler la fonction Manger() associée au type de l'objet sur lequel pointe p_animal » (voir la section « Accès aux membres d'une classe » ci-après). Même si vous ne connaissez pas le type d'animal qui sera traité dans cette instruction, vous êtes assuré que la bonne version de la fonction Manger() sera exécutée et que vous ne donnerez pas de viande à vos perroquets !

Convention d'affectation des noms : "affectation:des noms (convention)"

Vous avez certainement remarqué l'utilisation particulière des majuscules et minuscules dans les noms de classe, de membre ou de méthode. Le langage C++ fait la différence entre les majuscules et les minuscules. Vous pouvez utiliser tous les caractères de l'alphabet, des chiffres et le caractère _ (underscore) dans le nom des objets, mais ce nom ne doit pas commencer par un chiffre et il ne doit pas contenir de caractères spéciaux (lettre accentuée, opérateur, caractère de ponctuation, etc.).
Dans cet ouvrage, nous avons adopté la convention propre au Java, qui consiste à attribuer une majuscule aux noms de classes (Lion, Singe, Animal, etc.) et aux noms de méthodes (LireAge(), Dormir()), et à écrire les noms de variables en minuscules. Si le nom est composé de plusieurs mots, alors ils seront reliés par un underscore _ (couleur_pelage, longueur_queue, etc.).

Le texte original de cette fiche pratique est extrait de
«Tout sur le C++» (Christine EBERHARDT, Collection
CommentCaMarche.net, Dunod, 2009)

A voir également :

Ce document intitulé «  c++ - l'orienté objet  » issu de CommentCaMarche (www.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.