Copyright © 2000 Michael Williams
Copyright © 2009 Ly-Sunnaram Hervé
Article paru dans le n°55 de la Gazette Linux de juillet 2000.
Article publié sous Open Publication License. La Linux Gazette n'est ni produite, ni sponsorisée, ni avalisée par notre hébergeur principal, SSC, Inc.
Table des matières
C'est classe comme titre, hein ? Hé non, c'est pas fait pour être classe, mais pour informer. Comme il l'indique, cet article (ou tutoriel si vous voulez) est entièrement consacré à la POO (Programmation Orientée Objet) dans le langage de programmation C++. Bien, passons aux présentations. Je m'appelle Williams, Mike Williams. Ma mission ? Apprendre aux programmateurs débutants, tels que vous, l'art de la programmation. Au fil des mois, j'espère vous faire découvrir diverses techniques de programmations en commençant ici et maintenant avec le C++. Êtes-vous bien assis ? Très bien, je vais commencer.
La POO est incontestablement l'une des techniques de programmation les plus compliquées à expliquer. En fait, c'est plus une toute nouvelle manière de voir la programmation plutôt qu'une ' technique '. Il y a des livres entiers consacrés à ce sujet, mais une introduction à la philosophie et aux implications de la POO dépasse largement le cadre de cet article. Pour comprendre la POO, vous devez d'abord comprendre ce qu'était la programmation avant la POO.
A cette époque-là, la définition élémentaire de la programmation était la suivante: un programme est une séquence d'instructions logiques suivie par l'ordinateur. Et c'est tout. C'est bien beau mais regardons les choses en face, ça ne nous inspire pas trop. Jusqu'à maintenant, en tout cas. La POO est restée dans l'ombre pendant pas mal de temps jusqu'à ce jour mais ça y est, on s'y intéresse. Dans les langages de programmation OO (Orienté Objet), l'accent est surtout mis sur les données, ou les ' objets ' utilisés et comment le programmeur les manipulent. Avant la POO, les nombres n'étaient que des adresses en mémoire, une suite d'octets qui ne voulaient rien dire. Et maintenant, avec la POO, ils sont devenus bien plus que ça. Le programme est maintenant une solution à tout problème que l'on peut rencontrer quel qu'il soit mais elle est élaborée par rapport aux objets qui définissent votre problème et en utilisant les fonctions liées à ces objets. Vous êtes perdus? Ne vous inquiétez pas, vous n'aurez pas besoin de comprendre la POO pour l'utiliser dans vos programmes. En effet, la meilleure manière de savoir ce qu'est la POO est de l'utiliser lorsque vous programmez.
Tous les exemples de cet article peuvent être compilés avec le compilateur GNU C++. Pour l'utiliser, tapez:
g++ nom_du_fichier
dans le terminal BASH. Je pars du principe que vous avez un compilateur à peu près à jour mais ça ne devrait pas changer grand chose si ce n'est pas le cas. Au fait, vous ne pouvez pas utiliser le compilateur GNU C, ça ne marchera pas (je pense qu'il fallait que je le signale). Vous aurez besoin bien évidemment d'un éditeur de texte. ' Emacs ' est un éditeur très puissant et je vous conseille de l'utiliser.
Cet article est destiné aux personnes qui ont déjà des connaissances correctes du langage C++ et qui veulent approfondir ces connaissances en s'intéressant à la POO en C++. Si vous êtes un vrai débutant, je vous propose de lire l'un des tutoriels de C++ sur les centaines qui trainent sur Internet. Je vous conseille de commencer en allant sur http://www.programmingtutorials.com/ . Bon courage!
Il y a des centaines d'années, en Grande Bretagne (plus précisément en Angleterre), il y avait des troubles civils. Les gens étaient furieux, les pauvres pour être plus précis. Ils avaient remarqué que certaines personnes étaient plus riches qu'eux et ça ne leur plaisait pas. Comment régler ce problème? Comment assurer le bonheur du peuple? La religion avait déjà avancé sur ce sujet mais même la promesse du paradis pour les pauvres qui se tenaient bien dans leur vie ne semblait pas marcher. Le capitalisme avait déjà lancé ses tentacules dans le monde et il fallait trouver une nouvelle idée pour assurer le bonheur des masses. Cette idée apparut sous le nom de ' classe '. L'idée de base était que si tout le monde connaissait sa place et son rôle dans la société, alors tous se sentiraient en sécurité et heureux et personne ne défierait l'autorité. Ça a marché. On eût la bourgeoisie (qui était riche), la classe moyenne (qui n'était pas aussi riche) et la classe ouvrière (qui avait à peine les moyens de vivre). C'était fort injuste mais c'est pourtant bien ce qu'est devenu le monde. Vous me demandez ce que ça a à voir avec le C++? Eh bien en C++, toute la partie Orientée Objet existe sous forme de classe. Mais ça suffit, nous sommes des programmeurs, pas des spécialistes des sciences sociales.
Jusqu'à présent, vous n'avez utilisé que des types de variable de base lorsque vous utilisiez le C++ : int, float, bool, double et ainsi de suite. On les appelle type de données simple. Cependant, ils sont très limités pour ' modéliser ' ce que l'on veut. Prenons un exemple. Disons que nous voulons représenter un objet de la vie réelle, une maison par exemple. Il est clair qu'il va falloir étudier les différentes caractéristiques d'une maison : le nombre de pièces qu'elle a, son numéro de rue et si elle a un jardin ou non (d'accord, il y a bien d'autres caractéristiques mais je ne veux pas les recenser maintenant). En C++, on peut représenter la maison de cette façon
int numero, pieces; bool jardin;
Et ça marcherait très bien pour cet exemple particulier. Mais supposons maintenant que nous voulons beaucoup de maisons. Supposons que nous voulons faire un programme bien plus compliqué que ça. Supposons que nous voulons définir notre propre type de données pour représenter la maison. Le C++ nous le permet grâce à l'utilisation de classe.
Continuons avec notre exemple de la maison. Regardons comment nous pourrions ' modéliser ' une maison en utilisant une classe C++:
class maison { public: int numero, pieces; bool jardin; }; main() { maison ma_maison; ma_maison.numero=40; ma_maison.pieces=8; ma_maison.garden=1; return 0; }
Voyons ce que chaque ligne fait. La deuxième ligne déclare une nouvelle classe et la nomme ' maison '. Nous ouvrons ensuite la définition de la classe avec une accolade '{'. La ligne suivante déclare que tous les 'membres' (tous les types de données appartenant à la classe) sont public (j'expliquerai ce que ça signifie plus loin). Nous déclarons ensuite deux variables du type de base 'int' (integer). La déclaration suivante est le membre jardin qui est de type bool ( booléen : soit 1 soit 0). Enfin, nous terminons la classe par une accolade fermante ' } ' et un ' ; '. Nous avons maintenant déclaré un nouveau type de données ou classe appelée ' maison ' que nous pouvons utiliser dans notre programme. Pour l'utiliser, on écrit la fonction main() qui sera l'endroit où l'exécution du programme commence et finit. La première chose que nous faisons dans cette fonction est de déclarer la variable 'ma_maison' comme étant de type 'maison'. C'est la classe que nous avons définie au début du programme. On peut voir que cette variable a gagné plusieurs dimensions. Elle a maintenant bien plus d'attributs qu'un simple type int ou float. De par notre définition de classe, nous avons donné à la maison trois variables: numero, pieces et jardin. La variable que nous venons de déclarer, ma_maison, contient tous ces attributs. Dans la seconde ligne de notre fonction main, nous affectons au membre numero de l'objet ma_maison la valeur 40. Nous affectons ensuite des valeurs aux deux autres membres de l'objet ma_maison avant de finir la fonction avec la valeur de retour 0.
Arrivé là, vous êtes assis à vous demander pourquoi on fait tant d'histoire avec ces classes. Après tout, ne serait-ce pas plus simple d'utiliser la méthode non Orientée Objet? Eh bien, ça le serait dans ce cas particulier vu que nous parlons d'un tout petit programme de rien du tout. Cependant, une fois qu'on commence à écrire des programmes de plus en plus compliqués, on trouve non seulement que les classes sont utiles mais qu'en plus, elles sont indispensables.
C'est bien beau de pouvoir déclarer quelques variables mais comment allons nous les utiliser? La réponse arrive bien sûr avec les fonctions. En C++, les classes peuvent avoir des fonctions membres. Elles sont déclarées tout comme le sont les variables membres. Pour illustrer leur fonctionnement, prenons un exemple. Pourquoi pas un carré. Tout d'abord, nous devons modéliser les données basées sur les caractéristiques d'un carré. Il a une longueur, une largeur et bien évidemment une surface. Naturellement, vous trouvez la surface du carré en multipliant la longueur par la largeur. Pour cela, nous pouvons utiliser une fonction membre :
class carre { public: int longueur, largeur; int surface() { return longueur*largeur; } }; main() { carre mon_carre; mon_carre.longueur=5; mon_carre.largeur=2; cout<<mon_carre.surface(); return 0; }
Cet exemple devrait retourner la valeur 10. La classe carre ressemble beaucoup à la classe maison que nous avons vue plus tôt. Premièrement, nous avons déclaré deux variables membres de type int: longueur et largeur. Nous avons ensuite déclaré une fonction, surface(), qui retourne un int. On déclare une fonction exactement comme en dehors d'une classe. Ici, nous faisons en sorte que la fonction surface() nous retourne la valeur de la longueur multipliée par celle de la largeur. Nous avons ensuite fermé la classe et écrit la fonction main, ce qu'il n'est pas nécessaire d'expliquer.
Évidemment, si vous avez beaucoup de fonctions à mettre dans la classe, cela va faire un gros fouillis. Pour éviter ça, nous utilisons quelque chose qui s'appelle opérateur de résolution de portée. Disons que nous voulons déclarer la fonction surface() en dehors de notre définition de fonction d'origine. Premièrement, nous allons déclarer la classe carre et à l'intérieur la fonction surface, comme on l'a montré plus haut. Cependant, nous n'insèrerons pas le code de la fonction pour le moment. Voici la définition de classe que cela va donner :
class carre { public: int longueur, largeur; int surface(); }
Pour définir la fonction membre surface() hors de la définition de la classe, nous écrirons ceci:
int carre::surface() { return longueur*largeur; }
Ce nouveau code retournera la même chose.
Pendant que nous sommes sur le sujet des définitions de fonctions membres, il est nécessaire de connaître la différence entre des membres public (public) et private (privé) d'une classe. Les membres qui sont déclarés public sont accessibles par toutes fonctions dans le programme en entier. La façon la plus simple de l'expliquer est d'utiliser un exemple. Supposons que nous déclarons la classe carré comme elle l'était ci-dessus et que nous essayons d'accéder à la variable longueur depuis la fonction main, qui n'est pas une fonction membre de la classe:
main() { carre mon_carre; mon_carre.longueur=2; cout<<mon_carre.longueur; return 0; }
Cela ne posera aucun problème au compilateur et renverra la valeur 2. Cependant, disons que l'on change la classe carre de telle façon qu'elle ressemble à ce qui suit et que les membres soient privés:
class carre { private: int longueur, largeur; int surface(); }
Si nous essayons de lancer la fonction main() montrée ci-dessus, le compilateur génèrera une erreur. On ne peut accéder aux membres privés que par des fonctions membres.
C'est un petit peu fastidieux à la longue de déclarer les valeurs de chaque variable membre en utilisant la méthode montrée ci-dessous:
main() { carre mon_carre; mon_carre.longueur=2; mon_carre.largeur=3; }
Pour chaque membre de mon_carre, nous devons déclarer et initialiser séparément les valeurs. Il est clair que non seulement c'est fastidieux mais il est également facile d'oublier d'initialiser un membre, en particulier lorsque votre classe devient plus complexe. Une façon de palier à ce problème est d'utiliser un constructeur de classe. Un constructeur de classe est une fonction qui s'initialise dès qu'une classe est utilisée:
class carre { public: int longueur, largeur; carre(int longueur1, int largeur1) { longueur=longueur1; largeur=largeur1; } int surface() { return longeur*largeur; } } main() { carre mon_carre(2,5); cout<<mon_carre.area(); return 0; }
Ceci donnera en sortie la valeur 10. Tout d'abord, nous déclarons le constructeur de classe en lui donnant le même nom que la classe. A partir de maintenant, cette fonction va s'exécuter dès que la classe est utilisée. Nous la déclarons de telle façon qu'elle prenne deux valeurs, toutes deux de types int. Le deuxième changement apparaît dans la fonction main(). A chaque fois que nous déclarons un objet de type carré, nous ajoutons la définition de la fonction. Dans notre exemple, nous donnons aux variables longueur1 et largeur1 les valeurs 5 et 2. Ensuite, le constructeur prend ces deux variables et les affectent aux variables membres longueur et largeur et tout se fait tout seul.
Il va sans dire que vous pouvez utiliser des tableaux avec vos classes. Évidemment, ça nous ouvre la possibilité de déclarer bien plus de variables en bien moins de temps. Il n'y a pas grand chose à dire sur le sujet donc prenons un exemple simple:
class personne{ public: int age, numero_de_maison; }; main(){ personne alex[5]; for(int x(0);x<5;x++) { alex[x].age=x; alex[x].numero_de_maison=x; cout<<"L'age est "<<alex[x].age<<endl <<"Le numero de maison est " <<alex[x].numero_de_maison<<endl; } return 0; }
Il n'y a rien de compliqué dans cet exemple donc je ne m'étendrai pas là-dessus. Évidemment, on peut faire beaucoup plus de choses avec des tableaux et il n'y a pas besoin d'être un génie pour trouver d'autres façons de les utiliser.
Eh bien c'est tout pour ce mois-ci. Si j'en ai l'occasion, je continuerai ce même sujet la semaine prochaine et j'irai plus loin dans la POO en C++ en abordant les notions comme les pointeurs, les destructeurs de classe, l'héritage et l'organisation de vos codes dans des fichiers. Bonne programmation!