[Plan du site]
Vous êtes ici ---
> Le Site du Zéro
> Les tutoriels
> Officiels
> Programmation
> Apprenez à programmer en C++ ! > [Théorie] La Programmation Orientée Objet > La magie de la POO par l'exemple : string
> Lecture du tutoriel
La magie de la POO par l'exemple : string
Nous attaquons maintenant la 2ème moitié de la première partie du cours de C++. Et comme dans la vie rien n'est jamais simple, cette "deuxième moitié" sera la plus grosse et... la plus délicate aussi
Nous allons maintenant, et dans les chapitres suivants, 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. Ca ne va pas révolutionner immédiatement vos programmes, ça va vous paraître un peu inutile au début (comme lorsque vous aviez découvert les pointeurs

), mais faites-moi confiance : faites l'effort de faire ce que je dis à la lettre, et bientôt vous serez bien plus efficaces lorsque vous programmerez.
Ce chapitre va vous parler des 2 facettes de la POO, le côté
utilisateur et le côté
créateur.
Puis, je vais faire carrément l'inverse de ce que tous les cours de programmation font (je sais je suis fou

) : au lieu de commencer par vous apprendre à
créer des objets, je vais d'abord vous montrer comment les
utiliser avec pour exemple le type
string fourni par le langage C++.
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 des éléments que l'on appelle des "objets" dans son code source.
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 ce que je vous raconte ressemble à un traité d'art moderne conceptuel, on va imaginer ensemble ce qu'est un objet à l'aide de plusieurs schémas concrets.
Les schémas 3D que vous allez voir par la suite ont été réalisés pour moi par l'ami Nab, que je remercie d'ailleurs vivement au passage.
Imaginez qu'un programmeur 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 va avoir besoin de plusieurs fonctions qui s'appellent entre elles, et de variables pour mémoriser la position, la taille de la fenêtre, etc.
Il met du temps à écrire ce code, c'est un peu compliqué, mais il y arrive. Au final, le code qu'il a écrit est composé de plusieurs fonctions et variables. Quand on regarde ça pour la première fois, ça ressemble à une expérience de savant fou à laquelle on ne comprend rien :
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 passer du temps à tout réécrire. Seulement voilà, à moins d'être un expert en chimie certifié, vous allez mettre pas mal de temps avant de comprendre comment tout ce bazar fonctionne.
Quelle fonction appeler en premier ? Quelles valeurs envoyer à quelle fonction pour redimensionner la fenêtre ?
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 :
Ici, une partie du cube a été volontairement mise en transparence pour 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 :
Ce cube contient toutes les fonctions et les variables (nos fioles de chimie), mais il les
masque à l'
utilisateur.
Au lieu d'avoir des tonnes de tubes et fioles chimiques 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'à se servir des boutons du cube et n'a plus besoin de se soucier de tout ce qui se passe à l'intérieur. Pour l'utilisateur, c'est donc complètement simplifié.
En clair : programmer de manière orientée objet, c'est
créer du code source (peut-être 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 le programmeur qui va l'
utiliser, travailler avec un objet est donc beaucoup plus simple qu'avant : il a juste à appuyer sur des boutons et 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. Nous allons créer des objets de type
string. Le type string est fourni par la librairie standard du C++.
Ce qui va suivre devrait vous convaincre une fois pour toutes que travailler avec des objets, bon sang que c'est simple

(bon les créer sera une autre paire de manches, mais ne gâchons pas la fête qui va suivre

).
Vous vous souvenez des chaînes de caractères ? Vous vous souvenez de ce chapitre un peu compliqué où je vous avais dit qu'une chaîne était un tableau de char, que toute chaîne devait se terminer par un \0, qu'il fallait bien calculer la longueur de son tableau lorsqu'on le déclarait pour ne pas oublier la place pour l'\0 ?
Ça avait donné lieu à des schémas comme celui-ci, pour expliquer par exemple la concaténation de 2 chaînes :
Bon, soyons clairs, en C++ votre ordinateur ne fonctionne pas différemment

C'est toujours aussi complexe à gérer (et parfois dangereux si vous omettez l'\0)... Mais si on gérait les chaînes comme des objets ? Si au lieu d'avoir tout ce bazar d'\0 et de longueurs de tableaux à gérer, on plaçait tout ça dans un gros cube (un objet) ?
Notre cube proposerait en façade plusieurs boutons pour faire toutes les opérations possibles et imaginables avec des chaînes de caractères, tout en nous évitant d'avoir à savoir comment ça fonctionne à l'intérieur.
Ce type d'objet existe, il est déjà créé et il est livré dans la librairie standard du C++. Il s'appelle
string ("chaîne" en anglais).
Votre premier objet
Allez n'attendons plus, et voyons comment on crée un objet de type string dans un code source
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12 | #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 qu'il est nécessaire d'inclure le header de la librairie
string pour pouvoir utiliser des objets de type string dans le code

C'est ce que j'ai fait à la 2ème ligne.
Intéressons-nous maintenant à la ligne où je crée un objet de type string...
Mais... 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. 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 :
- Si c'est une variable, la première lettre du type doit être en minuscule (ex : int)
- Si c'est un objet, la première lettre du type doit être en majuscule (ex : Voiture)
Je sais ce que vous allez me dire : "
string ne commence pas par une majuscule !". Il faut croire que ceux qui ont créé string ne respectaient pas cette convention. Mais rassurez-vous, maintenant la plupart des gens mettent une majuscule au début de leurs objets (dont moi), ça sera pas la foire dans la suite de ce cours
Affecter une valeur à la chaîne lors de la déclaration
Pour affecter une valeur à notre objet au moment de la déclaration, il y a plusieurs possibilités. La plus courante consiste à ouvrir des parenthèses comme si on appelait une fonction :
Code : C++1
2
3
4
5
6 | int main()
{
string maChaine("Bonjour !"); // Création d'un objet "maChaine" de type string et affectation
return 0;
}
|
(vous commencez à comprendre ce que je vous racontais tout à l'heure quand je disais que les objets étaient une sorte de mélange de variables et fonctions
)
Dans la plupart des cas donc, on ouvrira des parenthèses pour affecter une valeur à l'objet lors de sa création. Pour le type string cependant, il est possible de faire encore plus simple en utilisant le symbole égal :
Code : C++1
2
3
4
5
6 | int main()
{
string maChaine = "Bonjour !"; // Création d'un objet "maChaine" de type string et affectation
return 0;
}
|
Bref, voilà qui est fait. On a un objet maChaine qui contient la chaîne "Bonjour !".
On peut l'afficher comme n'importe quelle chaîne de caractères avec un cout :
Code : C++1
2
3
4
5
6
7 | int main()
{
string maChaine = "Bonjour !";
cout << maChaine << endl; // Affichage du string comme si c'était une chaîne de caractères
return 0;
}
|
Code : Console
Affecter une valeur à la chaîne après déclaration
Maintenant que notre objet est créé, ne nous arrêtons pas là. Changeons la chaîne qui se trouve à l'intérieur. Donnez-lui la chaîne que vous voulez, il la stockera :
Code : C++ 1
2
3
4
5
6
7
8
9
10 | int main()
{
string maChaine = "Bonjour !";
cout << maChaine << endl;
maChaine = "Bien le bonjour !";
cout << maChaine << endl;
return 0;
}
|
Code : Console | Bonjour !
Bien le bonjour ! |
Mais... on n'a pas précisé la longueur de la chaîne au début, et maintenant on stocke dedans une chaîne plus grande qu'avant ! Comment on sait s'il y aura la place de stocker une chaîne aussi longue ?
C'est là que 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 change pour
Bien le bonjour !". A l'intérieur de l'objet, des mécanismes (des fonctions) se sont activées lorsque vous avez fait ça. Ces fonctions ont vérifié entre autres s'il y avait de la place pour stocker la chaîne. 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 elles ont détruit l'ancien tableau qui ne servait plus à rien, tant qu'à faire.
Et permettez-moi de vous parler franchement : ce qui s'est passé à l'intérieur de l'objet, on s'en fout royalement

C'est bien là tout l'intérêt de la POO : l'
utilisateur n'a pas besoin de comprendre comment ça marche à l'intérieur. L'objet est en quelque sorte intelligent et gère tous les cas. Nous, on ne fait que l'
utiliser ici.
Du coup, pour nous c'est simplifié comme vous avez pas idée

Avant, on ne pouvait pas affecter une chaîne avec le signe égal (sauf au moment de la déclaration), maintenant on peut le faire à n'importe quel moment ! Et on n'a plus à se soucier de la taille du tableau, elle est automatiquement recalculée !
Et ça, c'est rien qu'un début
Concaténation de chaînes
Allez, je vais continuer à vous faire baver

On va concaténer (assembler) 2 chaînes. Regardez comment on fait :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11 | 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;
}
|
Code : Console | Bonjour !Comment allez-vous ? |
Ah, allez je reconnais, il manque un espace au milieu. On n'a qu'à changer la ligne de la concaténation :
Code : C++1 | chaine3 = chaine1 + " " + chaine2;
|
Résultat :
Code : Console | Bonjour ! Comment allez-vous ? |
Niveau de simplicité : effarante

Avant, il aurait fallu appeler une fonction pour la concaténation, et faire attention à la longueur de la chaîne qui est modifiée pour être sûr qu'il y ait suffisamment de place dedans. Ici, rien de tout cela
On assemble donc nos chaînes de caractères à l'aide du symbole +. Et avouez franchement, si vous avez un tant soit peu programmé avant, que ça va vous faire gagner un temps de fou
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 un if !).
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | 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;
}
|
Code : Console | Les chaines sont differentes |
C'est tout bête

Vous pouvez vérifier que ça marche aussi dans le cas contraire :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | int main()
{
string chaine1 = "Bonjour !";
string chaine2 = "Bonjour !";
if (chaine1 == chaine2) // Vrai
{
cout << "Les chaines sont identiques" << endl;
}
else
{
cout << "Les chaines sont differentes" << endl;
}
return 0;
}
|
Code : Console | Les chaines sont identiques |
Si ça vous amuse, testez aussi le symbole "différent de" (
!=), vous verrez que 2 objets de type string sont capables de se comparer entre eux
Conclusion : plus simple tu meurs
Le type string ne s'arrête pas à ce que nous venons de voir. Comme tout bon 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 toutes les fonctionnalités des string en revue (elles 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 façade avant du cube
Appeler une méthode d'un objet se fait de la même manière qu'avec les structures. On utilise le point pour séparer l'objet de sa méthode :
objet.methode()
En théorie, on peut aussi accéder aux variables membres (les "attributs") de l'objet de la même manière qu'on le faisait avec les structures. Cependant, en POO, il y a une règle super importante qui dit que l'utilisateur ne doit pas pouvoir accéder aux variables membres, mais seulement aux fonctions membres. On en reparlera dans le prochain chapitre plus en détail.
Quelques méthodes utiles du type string
La méthode size()
La méthode size() permet de connaître la longueur de la chaîne actuellement stockée dans l'objet de type string. C'est un peu l'équivalent de strlen(), mais pour les string cette fois
Cette méthode ne prend aucun paramètre et renvoie la longueur de la chaîne. Comme vous venez de le découvrir, il va falloir appeler la méthode de la manière suivante :
Code : C++
Essayons ça dans un code complet qui affiche la longueur de la chaîne :
Code : C++1
2
3
4
5
6
7 | int main()
{
string maChaine = "Bonjour !";
cout << "Longueur de la chaine : " << maChaine.size();
return 0;
}
|
Code : Console | Longueur de la chaine : 9 |
Bingo !

C'est là toute la subtilité. Avant on aurait dû faire :
Code : C
La fonction strlen était valable pour n'importe quelle chaîne, mais il fallait préciser en paramètre à chaque fois quelle était la chaîne sur laquelle la fonction devait travailler.
Maintenant, l'ordre est un peu inversé. La fonction size() est contenue dans l'objet maChaine. Quand on l'appelle comme on vient de le faire, la fonction membre size() sait qu'elle doit calculer la longueur de la chaîne contenue dans l'objet où elle se trouve :
Code : C++
La fonction size() est donc propre à l'objet maChaine. Si vous créez un deuxième objet de type string, il y aura donc une autre fonction size() propre à cet autre objet.
Rassurez-vous, les compilateurs C++ sont suffisamment intelligents pour optimiser l'utilisation de la mémoire. Si vous créez 50 string, il n'y aura pas 50 fois la même fonction en mémoire (une seule suffit). Mais ce que je vous dis là, c'est ce qu'il faut "imaginer". Dans chaque objet (chaque boîte), il y a une fonction size() qui est propre à l'objet. C'est comme cela qu'il faut le voir.
La méthode erase()
Cette méthode très simple supprime tout le contenu de la chaîne :
Code : C++1
2
3
4
5
6
7
8 | int main()
{
string chaine = "Bonjour !";
chaine.erase();
cout << "La chaine contient : " << chaine << endl;
return 0;
}
|
Code : Console
Comme on pouvait s'y attendre, la chaîne ne contient plus rien
La méthode substr()
Une autre méthode qui peut s'avérer utile : substr(). Elle permet de ne prendre qu'une partie de la chaîne stockée dans un string.
substr signifie "substring", soit "sous-chaîne" en anglais.
Tenez, on va regarder son prototype, vous allez voir que c'est intéressant :
Code : C++1 | string substr( size_type index, size_type num = npos );
|
Cette méthode retourne donc un objet de type string. Ce sera la sous-chaîne après "découpage".
Elle prend 2 paramètres, ou plus exactement : 1 paramètre obligatoire, 1 paramètre facultatif. En effet,
num possède une valeur par défaut (npos) ce qui fait que le second paramètre ne doit pas obligatoirement être renseigné.
- index permet d'indiquer à partir de quel caractère on doit couper (ce doit être un numéro de caractère)
- num permet d'indiquer le nombre de caractères que l'on prend. Par défaut, la valeur est npos, ce qui correspond à 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
Code : C++1
2
3
4
5
6
7 | int main()
{
string chaine = "Bonjour !";
cout << chaine.substr(3) << endl;
return 0;
}
|
Code : Console
On a demandé à couper à partir du 3ème caractère (soit la lettre "j" vu que la première lettre correspond au caractère n°0).
On a volontairement omis le second paramètre facultatif, ce qui fait que du coup substr() a renvoyé tous les caractères restants avant la fin de la chaîne. Essayons de renseigner le paramètre facultatif pour ne pas prendre le point d'exclamation par exemple :
Code : C++1
2
3
4
5
6
7 | int main()
{
string chaine = "Bonjour !";
cout << chaine.substr(3, 4) << endl;
return 0;
}
|
Code : Console
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"
La méthode c_str()
Celle-là est un peu particulière, mais parfois fort utile. Son rôle ? Retourner un pointeur vers le tableau de char que contient l'objet de type string.
Quel intérêt me direz-vous ? En C++, à priori aucun intérêt.
Mais il peut (j'ai bien dit il "peut") arriver que vous deviez envoyer à une fonction un tableau de char classique, façon C. Dans ce cas, la méthode c_str() vous permet de récupérer un bon vieux tableau de char comme on faisait en C.
Code : C++ 1
2
3
4
5
6
7
8
9
10 | int main()
{
string chaine = "Bonjour !";
const char* chaineC = NULL;
chaineC = chaine.c_str(); // On récupère le tableau de char dans chaineC
cout << "La chaine contient : " << chaineC << endl; // On l'affiche pour vérifier que ça fonctionne
return 0;
}
|
Récupérer le tableau de char vous sera utile si vous devez envoyer une chaîne à une fonction à la base prévue pour le C qui ne reconnaît pas les string. C'est rare, mais ça arrive. Je préfère que vous sachiez qu'on a cette possibilité pour pas que vous soyez bêtement bloqué à un moment.
Autant que possible, utilisez des objets de type string plutôt que des tableaux de char : vous avez vu que c'était bien plus facile à utiliser
Comme le disait si bien ma prof d'informatique "
C'est plus confortable de travailler avec un string" (je vous
jure que c'est vrai, j'étais là

)
Bon plus sérieusement

Vous avez découvert le côté
utilisateur de la POO et à quel point ces nouveaux mécanismes pouvaient vous simplifier la vie.
Le côté
utilisateur est en fait le côté simple de la POO. Les choses se compliquent lorsqu'on passe du côté
créateur. Nous allons justement apprendre à créer des objets dans le prochain chapitre et tous les suivants. Une longue route pleine de péripéties nous attend