Jeux Libres
       
           

» Les Tutoriels » Apprenez à programmer en C++ ! » Introduction : la vérité sur les strings enfin dévoilée

Note : Vous vous apprêtez à lire un tutoriel de M@teo21 et Nanoc initialement publié à cette adresse sous la licence Creative Commons BY-NC-SA 2.0.

Introduction : la vérité sur les strings enfin dévoilée


Nous attaquons la 2ème partie du cours de C++. Et comme dans la vie rien n'est jamais simple, cette "deuxième moitié" sera la plus dense et... la plus délicate aussi. :D

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. Ça ne va pas révolutionner immédiatement vos programmes, ça va vous paraître un peu inutile au début, mais faites-moi confiance : faites l'effort de faire ce que je dis à la lettre, et bientôt vous trouverez cette manière de faire bien plus naturelle. Vous saurez plus aisément comment organiser vos programmes.

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 :p ) : 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++.





Chapitre précédent     Sommaire     Chapitre suivant


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 des éléments que l'on appelle des "objets" dans son code source.

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. o_O

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 ? Autrement dit : comment utiliser ce bazar sans qu'une fiole ne nous explose entre les mains ? :o

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 nous pencher sur le cas de string dans ce chapitre.

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 ça ?


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

Jusqu'ici, nous n'avons fait qu'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 string est très complexe. Horriblement complexe.


L'horrible secret du type string


Les enfants, il faut que je vous raconte une terrible vérité : le Père Noël n'existe pas ! Euh non... mauvaise fiche désolé. Ah oui, voilà ce que je voulais dire : le type string est affreusement plus complexe qu'il n'en a l'air au fond de ses entrailles.

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

Pour un ordinateur, les lettres n'existent pas



Comme nous l'avons vu, l'avantage des objets est de masquer la complexité du code au programmeur. 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 qu'à 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 sentiments. 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 ? (prononcez "aski")
C'est une table qui sert de convention pour convertir des nombres en lettres.

Un extrait de la table ASCII
NombreLettreNombreLettre
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 sont dans cette table. C'est pour ça que les caractères accentués ne sont pas utilisables de base 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 ça comme la lettre A ?


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

  • Si on utilise le type int pour stocker le nombre 65, l'ordinateur considèrera que c'est un nombre.
  • En revanche, si on utilise le type char pour stocker le nombre 65, l'ordinateur se dira "C'est la lettre A". Le type char (abréviation de character, "caractère" en français) est prévu pour stocker un caractère.

Le type char stocke donc un nombre qui est interprété comme un caractère.

Un char ne 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 l'on va voir...

Les textes sont des tableaux de char



Puisque char ne peut stocker qu'une seule lettre, les programmeurs ont eu l'idée de créer... un tableau de char ! Les tableaux permettant de retrouver plusieurs variables d'un même type côte à côte en mémoire, 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 de char comme ceci :
1
char texte[100];

... pour pouvoir stocker du texte (environ 100 caractères) !

Le texte n'est donc en fait qu'un assemblage de lettres en mémoire dans un tableau :


Chaque case correspond à un char. Tous ces char mis côte à côte forment du texte.

Attention : il faut prévoir suffisamment de place dans le tableau pour stocker tout le texte ! Ici, c'est un tableau de 100 cases, mais ça peut être juste si on veut stocker plusieurs phrases en mémoire !
Pour résoudre ce problème, on peut créer un très grand tableau (en prévision de la taille de ce qu'on va stocker), mais cela risque parfois de consommer beaucoup de mémoire pour rien.


Créer et utiliser 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 de char dont chaque case correspond à un caractère, il faut prévoir une taille suffisante pour stocker le texte que l'on souhaite sinon ça plante... Bref, ça fait beaucoup de choses auxquelles il faut penser.

Ca ne vous rappelle pas nos fioles chimiques ? Eh oui, tout ceci est aussi dangereux et compliqué qu'une expérience de chimiste. C'est là que la programmation orientée objet intervient : un développeur place le tout dans un cube facile à utiliser où il suffit d'appuyer sur des boutons. Ce cube, c'est l'objet string.

Créer un objet string



La création d'un objet ressemble beaucoup à la création d'une variable classique comme int ou double :
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; // Cration 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...

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 : "string ne commence pas par une majuscule alors que c'est un objet !". 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 ne sera pas la foire dans la suite de ce cours. ;)

Initialiser 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 :
1
2
3
4
5
6
int main()
{
   string maChaine("Bonjour !"); // Cration 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.

Et comme pour les variables, il faut noter qu'il est aussi possible d'initialiser avec le signe égal : string maChaine = "Bonjour !";


On a maintenant créé 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 :
1
2
3
4
5
6
7
int main()
{
   string maChaine("Bonjour !");
   cout << maChaine << endl; // Affichage du string comme si c'tait une chane de caractres
 
   return 0;
}
Bonjour !

Affecter 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 :
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;
}
Bonjour !
Bien le bonjour !

Pour changer le contenu d'une chaîne après sa déclaration, on doit obligatoirement utiliser le symbole "=".


Ca n'a l'air de rien, mais 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 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 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. On s'en moque que le texte soit stocké dans un tableau de char. L'objet est en quelque sorte intelligent et gère tous les cas. Nous, on ne fait que l'utiliser ici.

Concaténation de chaînes



Imaginez que l'on souhaite concaténer (assembler) 2 chaînes. En théorie c'est compliqué à faire car il faut fusionner 2 tableaux de char. En pratique, la POO nous permet de ne pas avoir à nous soucier du fonctionnement interne :
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... Concatnatioooooon
   cout << chaine3 << endl;
 
   return 0;
}
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 :
1
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 2 tableaux de char.

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 !).
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;
}
Les chaines sont differentes

A l'intérieur de l'objet, la comparaison se fait caractère par caractère entre les deux tableaux de char (à l'aide d'une boucle qui compare chacune des lettres). Nous, nous n'avons pas à nous soucier de tout cela : nous demandons à l'objet chaine1 s'il est identique à chaine2, il fait des calculs et nous répond très simplement par un oui ou un non. ;)


Opérations sur les string


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. ;)

On parle aussi de "variables membres" et de "fonctions membres".


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").

En théorie, on peut aussi accéder aux variables membres (les "attributs") de l'objet de la même manière. Cependant, en POO, il y a une règle très importante qui dit que l'utilisateur ne doit pas pouvoir accéder aux variables membres, mais seulement aux fonctions membres (les méthodes). 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.

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 :
1
maChaine.size()

Essayons ça dans un code complet qui affiche la longueur de la chaîne :
1
2
3
4
5
6
7
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 :
1
2
3
4
5
6
7
8
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 ;)

Notez que c'est équivalent à faire :
1
chaine = "";


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 :
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 :)
1
2
3
4
5
6
7
int main()
{
   string chaine("Bonjour !");
   cout << chaine.substr(3) << endl;
 
   return 0;
}
jour !

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 :
1
2
3
4
5
6
7
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 :
1
2
string chaine("Bonjour !");
cout << chaine[3] << endl;  //Affiche la lettre 'j'

On utilise substr() que si l'on a besoin d'accéder à plus d'une lettre à la fois.


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. On préfère largement manipuler un objet string plutôt qu'un tableau de char car 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 de char. Dans ce cas, la méthode c_str() vous permet de récupérer l'adresse du tableau de char qui se trouve à l'intérieur de l'objet string. Nous en avons eu besoin pour indiquer le nom du fichier à ouvrir dans un chapitre précédent, souvenez-vous :
1
2
3
string const nomFichier("C:/Nanoc/scores.txt");
 
ofstream monFlux(nomFichier.c_str());

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




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à). lol

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. ^^



Chapitre précédent     Sommaire     Chapitre suivant



Distribué et adapté par David
Consulté 4943 fois



Hébergeur du site : David
Version PHP : 5.4.45-0+deb7u2
Uptime : 240 jours 12 heures 10 minutes
Espace libre : 1569 Mo
Dernière sauvegarde : 22/07/2019
Taille de la sauvegarde : 1112 Mo


5357218 pages ont été consultées sur le site !
Dont 2689 pages pendant les 24 dernières heures.

Page générée en 0.574 secondes


Nos sites préférés
- Création d'un jeu de plateforme de A à Z avec SDL
- Zelda ROTH : Jeux amateurs sur le thème de Zelda
- Zeste de Savoir : la connaissance pour tous et sans pépins
- YunoHost : s'héberger soi-même en toute simplicité
- Site de Fvirtman : recueil de projets et de codes en C et C++
- Par ici la sortie : le site des idées de sorties


  © 2005-2019 linor.fr - Toute reproduction totale ou partielle du contenu de ce site est strictement interdite.