• 50 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

course.header.alt.is_certifying

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Mis à jour le 02/08/2019

Découvrez la notion de programmation orientée objet (POO)

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Nous allons découvrir la notion de programmation orientée objet (POO). Comme je vous l'ai dit plus tôt, c'est une nouvelle façon de programmer. Cela ne va pas immédiatement révolutionner vos programmes, cela va même vous paraître un peu inutile au début mais ayez confiance : faites l'effort de suivre mes indications à la lettre et, bientôt, vous trouverez cette manière de coder bien plus naturelle. Vous saurez plus aisément organiser vos programmes.

Ce chapitre va vous parler des deux facettes de la POO : le côté utilisateur et le côté créateur.
Puis je vais faire l'inverse de ce que font tous les cours de programmation : au lieu de commencer par vous apprendre à créer des objets, je vais d'abord vous montrer comment les utiliser, en basant mes exemples sur le type string fourni par le langage C++.

Des objets... pour quoi faire ?

Ils sont beaux, ils sont frais mes objets

S'il y a bien un mot qui doit vous frustrer depuis que vous en entendez parler, c'est celui-ci : objet.

Encore un concept mystique ? Un délire de programmeurs après une soirée trop arrosée ?
Non parce que, franchement, un objet, c'est quoi ? Mon écran est un objet, ma voiture est un objet, mon téléphone portable... Ce sont tous des objets !

Bien vu, c'est un premier point.
En effet, nous sommes entourés d'objets. En fait, tout ce que nous connaissons (ou presque) peut être considéré comme un objet. L'idée de la programmation orientée objet, c'est de manipuler dans son code source des éléments que l'on appelle des « objets ».

Voici quelques exemples d'objets dans des programmes courants :

  • une fenêtre ;

  • un bouton ;

  • un personnage de jeu vidéo ;

  • une musique.

Comme vous le voyez, beaucoup de choses peuvent être considérées comme des objets.

Mais concrètement, c'est quoi ? Une variable ? Une fonction ?

Ni l'un, ni l'autre. C'est un nouvel élément en programmation.
Pour être plus précis, un objet c'est... un mélange de plusieurs variables et fonctions.

Ne faites pas cette tête-là, vous allez découvrir tout cela par la suite.

Imaginez... un objet

Pour éviter que mes explications ne ressemblent à un traité d'art contemporain conceptuel, nous allons imaginer ensemble ce qu'est un objet à l'aide de plusieurs schémas concrets.

Imaginez qu'un développeur décide un jour de créer un programme qui permet d'afficher une fenêtre à l'écran, de la redimensionner, de la déplacer, de la supprimer... Le code est complexe : il aura besoin de plusieurs fonctions qui s'appellent entre elles, ainsi que de variables pour mémoriser la position, la taille de la fenêtre, etc.
Le développeur met du temps à écrire ce code, c'est un peu compliqué mais il y arrive. Au final, le code qu'il a rédigé est composé de plusieurs fonctions et variables. Quand on regarde le résultat pour la première fois, cela ressemble à une expérience de savant fou à laquelle on ne comprend rien.

Le code ressemble à une expérience complexe
Le code ressemble à une expérience complexe

Ce programmeur est content de son code et veut le distribuer sur Internet, pour que tout le monde puisse créer des fenêtres sans perdre du temps à tout réécrire. Seulement voilà, à moins d'être un expert certifié en chimie, vous allez mettre pas mal de temps avant de comprendre comment fonctionne tout ce bazar.

Quelle fonction appeler en premier ? Quelles valeurs envoyer à quelle fonction pour redimensionner la fenêtre ? Autrement dit : comment utiliser ce fatras sans qu'une fiole ne nous explose entre les mains ?

C'est là que notre ami programmeur pense à nous. Il conçoit son code de manière orientée objet. Cela signifie qu'il place tout son bazar chimique à l'intérieur d'un simple cube. Ce cube est ce qu'on appelle un objet.

L'utilisation du code est simplifiée grâce à l'utilisation d'un objet
L'utilisation du code est simplifiée grâce à l'utilisation d'un objet

Sur la figure suivante, une partie du cube a été volontairement mise en transparence afin de vous montrer que nos fioles chimiques sont bien situées à l'intérieur du cube. Mais en réalité, le cube est complètement opaque, on ne voit rien de ce qu'il y a à l'intérieur.

Le code est en réalité totalement invisible pour l'utilisateur
Le code est en réalité totalement invisible pour l'utilisateur

Ce cube contient toutes les fonctions et variables (nos fioles de chimie) mais il les masque à l'utilisateur.

Au lieu d'avoir des tonnes de tubes et de fioles dont il faut comprendre le fonctionnement, on nous propose juste quelques boutons sur la face avant du cube : un bouton « ouvrir fenêtre », un bouton « redimensionner », etc. L'utilisateur n'a plus qu'à employer les boutons du cube, sans se soucier de tout ce qui se passe à l'intérieur. Pour lui, le fonctionnement est donc complètement simplifié.

En clair, programmer de manière orientée objet, c'est créer du code source (potentiellement complexe) mais que l'on masque en le plaçant à l'intérieur d'un cube (un objet) à travers lequel on ne voit rien. Pour la personne qui va l'utiliser, travailler avec un objet est donc beaucoup plus simple qu'avant : il suffit d'appuyer sur des boutons et on n'a pas besoin d'être diplômé en chimie pour s'en servir.

Bien sûr, c'est une image, mais c'est ce qu'il faut comprendre et retenir pour le moment.

Nous n'allons pas voir tout de suite comment faire pour créer des objets, en revanche nous allons apprendre à en utiliser un. Dans ce chapitre, nous allons nous pencher sur le cas de string.

J'ai déjà utilisé le type string, ce n'est pas une nouveauté pour moi ! C'est le type qui permet de stocker du texte en mémoire, c'est cela ?

Oui. Mais comme je vous l'ai dit il y a quelques chapitres, le type stringest différent des autres. int,bool,float,doublesont des types naturels du C++. Ils stockent des données très simples. Ce n'est pas le cas de stringqui est en fait... un objet ! Le typestringcache bien des secrets à l'intérieur de sa boîte.

Jusqu'ici, nous nous sommes contentés d'appuyer sur des boutons (comme sur les schémas) mais, en réalité, ce qui se cache à l'intérieur de la boîte des objets stringest très complexe. Horriblement complexe.

L'horrible secret du type string

Grâce aux mécanismes de la programmation orientée objet, nous avons pu utiliser le type stringdès les premiers chapitres de ce cours alors que son fonctionnement interne est pourtant assez compliqué ! Pour vous en convaincre, je vais vous montrer comment fonctionne string« à l'intérieur du cube ». Préparez-vous à d'horribles vérités.

Pour un ordinateur, les lettres n'existent pas

Comme nous l'avons vu, l'avantage des objets est de masquer la complexité du code à l'utilisateur. Plutôt que de manipuler des fioles chimiques dangereuses, ils nous permettent d'appuyer sur de simples boutons pour faire des choses parfois compliquées.

Et justement, les choses sont compliquées parce que, à la base, un ordinateur ne sait pas gérer du texte ! Oui, l'ordinateur n'est véritablement qu'une grosse machine à calculer dénuée de sentiment. Il ne reconnaît que des nombres.

Mais alors, si l'ordinateur ne peut manipuler que des nombres, comment se fait-il qu'il puisse afficher du texte à l'écran ?

C'est une vieille astuce que l'on utilise depuis longtemps. Peut-être avez-vous entendu parler de la table ASCII (American Standard Code for Information Interchange, prononcé « aski ») ?
C'est un tableau qui sert de convention pour convertir des nombres en lettres.

Nombre

Lettre

Nombre

Lettre

64

@

96

'

65

A

97

a

66

B

98

b

67

C

99

c

68

D

100

d

69

E

101

e

70

F

102

f

71

G

103

g

72

H

104

h

73

I

105

i

74

J

106

j

75

K

107

k

76

L

108

l

77

M

109

m

Comme vous le voyez, la lettre « A » majuscule correspond au nombre 65. La lettre « a » minuscule correspond au nombre 97, etc. Tous les caractères utilisés en anglais figurent dans cette table. C'est pour cela que les caractères accentués ne sont, de base, pas utilisables en C++ : ils n'apparaissent pas dans la table ASCII.

Cela veut dire qu'à chaque fois que l'ordinateur voit le nombre 65, il prend cela pour la lettre A ?

Non, l'ordinateur ne traduit un nombre en lettre que si on le lui demande. En pratique, on se base sur le type de la variable pour savoir si le nombre stocké est véritablement un nombre ou, en réalité, une lettre :

  • Si on utilise le typeintpour stocker le nombre 65, l'ordinateur considérera que c'est un nombre.

  • En revanche, si on utilise le typecharpour stocker le nombre 65, l'ordinateur se dira « C'est la lettre A ». Le typechar(abréviation de character, « caractère » en français) est prévu pour stocker un caractère.

Le typecharstocke donc un nombre qui est interprété comme un caractère.

Uncharne peut stocker qu'un seul caractère ? Comment fait-on alors pour stocker une phrase entière ?

Eh bien, là non plus, ce n'est pas simple ! C'est un autre problème que nous allons voir...

Les textes sont des tableaux de char

Puisquecharne peut stocker qu'une seule lettre, les programmeurs ont eu l'idée de créer... un tableau dechar! Mais ça, vous le saviez déjà. Les tableaux permettant de retrouver côte à côte en mémoire plusieurs variables d'un même type, ils sont le moyen idéal de stocker du texte (on parle aussi de « chaînes de caractères », vous comprenez maintenant pourquoi).

Ainsi, il suffit de déclarer un tableau decharcomme ceci :

char texte[100];

... pour pouvoir stocker du texte (environ 100 caractères) ou d'utiliser un vector si l'on souhaite changer la longueur du texte:

vector<char> texte;

Le texte n'est donc en fait qu'un assemblage de lettres stocké en mémoire dans un tableau (figure suivante).

Une chaîne de caractères

Chaque case correspond à unchar. Tous cescharmis côte à côte forment du texte.

En théorie, on pourrait donc se débrouiller en utilisant des tableaux statiques ou dynamiques decharà chaque fois que l'on veut manipuler du texte. Mais ce serait fastidieux. C'est pour ça que les concepteurs du langage ont décidé de cacher tout ces mécanismes dans une boîte fermée, un objet.

Créez et utilisez des objets string

Vous venez d'en avoir un aperçu : gérer du texte n'est pas vraiment simple. Il faut créer un tableau dechardont chaque case correspond à un caractère, il faut prévoir une taille suffisante pour stocker le texte que l'on souhaite sinon cela plante... Bref, cela fait beaucoup de choses auxquelles il faut penser.

Cela ne vous rappelle-t-il pas nos fioles chimiques ? Eh oui, tout ceci est aussi dangereux et compliqué qu'une expérience de chimiste. C'est là qu'intervient la programmation orientée objet : un développeur place le tout dans un cube facile à utiliser où il suffit d'appuyer sur des boutons. Ce cube, c'est l'objetstring.
Reprenons quelques points que nous avons survolés au début de ce cours.

Créez un objetstring

Vous le savez déjà, la création d'un objet ressemble beaucoup à la création d'une variable classique commeintoudouble:

#include <iostream>
#include <string> // Obligatoire pour pouvoir utiliser les objets string
 
using namespace std;
 
 
int main()
{
    string maChaine; //Création d'un objet 'maChaine' de type string
 
    return 0;
}

Vous remarquerez pour commencer que, pour pouvoir utiliser des objets de typestringdans le code, il est nécessaire d'inclure l'en-tête de la bibliothèquestring. C'est ce que j'ai fait à la deuxième ligne.

Intéressons-nous maintenant à la ligne où je crée un objet de typestring...

Donc... on crée un objet de la même manière qu'on crée une variable ?

Il y a plusieurs façons de créer un objet, celle que vous venez de voir est la plus simple. Et, oui, c'est exactement comme si on avait créé une variable !

Mais mais... comment on fait pour différencier les objets des variables ?

C'est bien tout le problème : variables et objets se ressemblent dans le code. Pour éviter la confusion, il y a des conventions (qu'on n'est pas obligé de suivre). La plus célèbre d'entre elles est la suivante :

  • le type des variables commence par une minuscule (ex : int) ;

  • le type des objets commence par une majuscule (ex : Voiture).

Je sais ce que vous allez me dire : «stringne commence pas par une majuscule alors que c'est un objet ! ». Il faut croire que les créateurs destringne respectaient pas cette convention. Mais rassurez-vous, maintenant la plupart des gens mettent une majuscule au début de leurs objets (moi y compris, ce ne sera donc pas la foire dans la suite de ce cours).
En réalité, vous allez voir rapidement, une fois habitués à penser avec des objets, qu'une telle distinction n'est pas nécessaire. Il y a bien d'autres manières de distinguer les types des variables des types des objets. La première étant la façon dont les objets sont utilisés.

Initialisez la chaîne lors de la déclaration

Pour initialiser notre objet au moment de la déclaration (et donc lui donner une valeur !), il y a plusieurs possibilités. La plus courante consiste à ouvrir des parenthèses comme nous l'avons fait jusqu'ici :

int main()
{
    string maChaine("Bonjour !");
    //Création d'un objet 'maChaine' de type string et initialisation
 
    return 0;
}

C'est la technique classique que l'on connaît déjà et qui s'applique aussi bien aux variables qu'aux objets. On dit que l'on construit l'objet.

On a maintenant créé un objetmaChainequi contient la chaîne « Bonjour ! ».
On peut l'afficher comme n'importe quelle chaîne de caractères avec uncout:

int main()
{
    string maChaine("Bonjour !");
    cout << maChaine << endl;
    //Affichage du string comme si c'était une chaîne de caractères
 
    return 0;
}
Bonjour !
Affectez une valeur à la chaîne après déclaration

Maintenant que notre objet est créé, ne nous arrêtons pas là. Changeons le contenu de la chaîne après sa déclaration :

int main()
{
    string maChaine("Bonjour !");
    cout << maChaine << endl;
    
    maChaine = "Bien le bonjour !";
    cout << maChaine << endl;
 
    return 0;
}
Bonjour !
Bien le bonjour !

Tout cela, vous saviez déjà le faire. Si je vous répète ces bases ici, c'est pour vous montrer comment la magie de la POO opère. Vous, l'utilisateur, vous avez appuyé sur un bouton pour dire « Je veux maintenant que la chaîne à l'intérieur devienne « Bien le bonjour ! ». À l'intérieur de l'objet, des mécanismes (des fonctions) se sont activés lorsque vous réalisez l'opération. Ces fonctions ont vérifié, entre autres, s'il y avait de la place pour stocker la chaîne dans le tableau de char. Elles ont vu que non. Elles ont alors créé un nouveau tableau de char, suffisamment long cette fois, pour stocker la nouvelle chaîne. Et, tant qu'à faire, elles ont détruit l'ancien tableau qui ne servait plus à rien, puis les nouvelles lettres ont été copiées. Toute une histoire.

Et permettez-moi de vous parler franchement : ce qui s'est passé à l'intérieur de l'objet, on s'en fiche royalement !
C'est bien là tout l'intérêt de la POO : l'utilisateur n'a pas besoin de comprendre comment cela fonctionne à l'intérieur. On se moque de savoir que le texte est stocké dans un tableau de char(ou est-ce un vector ?). L'objet est en quelque sorte intelligent et gère tous les cas. Nous, nous nous contentons de l'utiliser.

Concaténation de chaînes

Imaginez que l'on souhaite concaténer (assembler) deux chaînes. En théorie, c'est compliqué à faire car il faut fusionner deux tableaux de char. En pratique, la POO nous évite de nous soucier du fonctionnement interne :

int main()
{
    string chaine1("Bonjour !");
    string chaine2("Comment allez-vous ?");
    string chaine3;
 
    chaine3 = chaine1 + chaine2; // 3... 2... 1... Concaténatioooooon
    cout << chaine3 << endl;
 
    return 0;
}
Bonjour !Comment allez-vous ?

Je le reconnais, il manque une espace au milieu. On n'a qu'à changer la ligne de la concaténation :

chaine3 = chaine1 + " " + chaine2;

Résultat :

Bonjour ! Comment allez-vous ?

C'est très simple à utiliser alors que derrière, les fioles chimiques s'activent pour assembler les deux tableaux dechar.

Comparaison de chaînes

Vous en voulez encore ? Très bien !
Sachez que l'on peut comparer des chaînes entre elles à l'aide des symboles==ou!=(que l'on peut donc utiliser dans unif!).

int main()
{
    string chaine1("Bonjour !");
    string chaine2("Comment allez-vous ?");
 
    if (chaine1 == chaine2) // Faux
    {
        cout << "Les chaines sont identiques." << endl;
    }
    else
    {
        cout << "Les chaines sont differentes." << endl;
    }
 
    return 0;
}
Les chaines sont differentes.

À l'intérieur de l'objet, la comparaison se fait caractère par caractère entre les deux tableaux dechar(à l'aide d'une boucle qui compare chacune des lettres). Nous, nous n'avons pas à nous soucier de tout cela : nous demandons à l'objetchaine1s'il est identique àchaine2; il fait des calculs et nous répond très simplement par un oui ou un non.
Ce que nous allons apprendre dans la suite de ce cours sont les recettes qui permettent aux programmeurs d'écrire de tels objets où toutes les opérations complexes sont cachées. Vous pourrez alors utiliser ces boîtes de fioles expérimentales cachées dans d'autres parties de votre programme sans avoir à vous soucier, ni même à vous rappeler du fonctionnement exact des fioles.

Opérations sur les string

Le type stringne s'arrête pas à ce que nous venons de voir. Comme tout objet qui se respecte, il propose un nombre important d'autres fonctionnalités qui permettent de faire tout ce dont on a besoin.

Nous n'allons pas passer en revue toutes les fonctionnalités des string(elles ne sont pas toutes indispensables et ce serait un peu long). Nous allons voir les principales, dont vous pourriez avoir besoin dans la suite du cours

Attributs et méthodes

Je vous avais dit qu'un objet était constitué de variables et de fonctions. En fait, on en reparlera plus tard mais le vocabulaire est un peu différent avec les objets. Les variables contenues à l'intérieur des objets sont appelées attributs et les fonctions sont appelées méthodes.

Imaginez que chaque méthode (fonction) que propose un objet correspond à un bouton différent sur la face avant du cube.

Pour appeler la méthode d'un objet, on utilise une écriture que vous avez déjà vue :objet.methode().

On sépare le nom de l'objet et le nom de la méthode par un point. Cela signifie « Sur l'objet indiqué, j'appelle cette méthode » (traduction : « sur le cube indiqué, j'appuie sur ce bouton pour déclencher une action »).

Je vous avais dit qu'il était facile de distinguer les objets des variables car on les utilisait différemment. Voici donc un premier exemple. La notation avec le point pour appeler une méthode de l'objet est propre aux objets. On ne peut pas faire ça avec les variables.

Quelques méthodes utiles du type string

La méthode size()

Vous le savez déjà, la méthode size()permet de connaître la longueur de la chaîne actuellement stockée dans l'objet de type string.

Cette méthode ne prend aucun paramètre et renvoie la longueur de la chaîne. Vous venez de découvrir qu'il s'agit d'une règle générale, mais nous l'avions déjà fait avant; il faut appeler la méthode de la manière suivante :

maChaine.size()

Essayons cela dans un code complet qui affiche la longueur d'une chaîne de caractères :

int main()
{
    string maChaine("Bonjour !");
    cout << "Longueur de la chaine : " << maChaine.size();
 
    return 0;
}
Longueur de la chaine : 9
La méthode erase()

Cette méthode très simple supprime tout le contenu de la chaîne :

int main()
{
    string chaine("Bonjour !");
    chaine.erase();
    cout << "La chaine contient : " << chaine << endl;
 
    return 0;
}
La chaine contient :

Comme on pouvait s'y attendre, la chaîne ne contient plus rien

La méthode substr()

Une autre méthode peut se révéler utile :substr(). Elle permet d'extraire une partie de la chaîne stockée dans unstring.

Tenez, on va regarder son prototype, vous allez voir que c'est intéressant :

string substr( size_type index, size_type num = npos );

Cette méthode renvoie donc un objet de typestring. Ce sera la sous-chaîne obtenue après « découpage ».
Elle prend deux paramètres ou, plus exactement, un paramètre obligatoire et un paramètre facultatif. En effet,numpossède une valeur par défaut (npos), ce qui fait que le second paramètre ne doit pas obligatoirement être renseigné. Voyons plus en détail ce qui se cache sous ces paramètres :

  • indexpermet d'indiquer à partir de quel caractère on doit couper (ce doit être un numéro de caractère).

  • numpermet d'indiquer le nombre de caractères que l'on prend. Par défaut, la valeur estnpos, ce qui revient à prendre tous les caractères qui restent. Si vous indiquez 2, la méthode ne renverra que 2 caractères.

Allez, un exemple sera plus parlant, je crois :

int main()
{
    string chaine("Bonjour !");
    cout << chaine.substr(3) << endl;
 
    return 0;
}
jour !

On a demandé à couper à partir du troisième caractère, soit la lettre « j », étant donné que la première lettre correspond au caractère n°0).
On a volontairement omis le second paramètre facultatif, ce qui fait quesubstr()a renvoyé tous les caractères restants jusqu'à la fin de la chaîne. Essayons de renseigner le paramètre facultatif pour exclure le point d'exclamation par exemple :

int main()
{
    string chaine("Bonjour !");
    cout << chaine.substr(3, 4) << endl;
 
    return 0;
}
jour

Bingo !
On a demandé à prendre 4 caractères en partant du caractère n°3, ce qui fait qu'on a récupéré « jour ».

Comme nous l'avions vu dans le chapitre sur les tableaux, il existe une autre manière de faire pour accéder à un seul caractère. On utilise les crochets[ ]comme pour les tableaux :

string chaine("Bonjour !");
cout << chaine[3] << endl;  //Affiche la lettre 'j'
La méthodec_str()

Cette méthode est un peu particulière mais parfois fort utile. Son rôle ? Renvoyer un pointeur vers le tableau decharque contient l'objet de typestring. :-°

Quel intérêt me direz-vous ? En C++, a priori aucun. On préfère largement manipuler un objetstringplutôt qu'un tableau decharcar c'est plus simple et plus sûr.

Néanmoins, il peut (j'ai bien dit il peut) arriver que vous deviez envoyer à une fonction un tableau dechar. Dans ce cas, la méthodec_str()vous permet de récupérer l'adresse du tableau decharqui se trouve à l'intérieur de l'objetstring. Dans un chapitre précédent, nous en avons eu besoin pour indiquer le nom du fichier à ouvrir, souvenez-vous :

string const nomFichier("C:/Nanoc/scores.txt");

ofstream monFlux(nomFichier.c_str());

L'usage dec_str()reste assez rare malgré tout.

Ce chapitre d'introduction à la POO se voulait simple pour ne pas vous effrayer. En réalité, vous avez appris à utiliser des objets (string et vector aussi !) dès le début de ce cours sans le savoir. C'est justement tout l'intérêt de ce paradigme. Cacher les aspects compliqués derrière une interface simple. J'espère que vous êtes prêt à inventer vos propres objets, car c'est ce que nous allons faire dans les prochains chapitres.

En résumé

  • La programmation orientée objet est une façon de concevoir son code. On considère qu'on manipule des objets.

  • Les objets sont parfois complexes à l'intérieur mais leur utilisation nous est volontairement simplifiée. C'est un des avantages de la programmation orientée objet.

  • Un objet est constitué d'attributs et de méthodes, c'est-à-dire de variables et de fonctions membres.

  • On appelle les méthodes de ces objets pour les modifier ou obtenir des informations.

  • La gestion du texte en mémoire est en fait complexe. Pour nous simplifier les choses, le langage C++ nous propose le typestring. Grâce à lui, nous pouvons créer des objets de typestringet manipuler du texte sans avoir à nous soucier du fonctionnement de la mémoire.

Exemple de certificat de réussite
Exemple de certificat de réussite