Vous êtes sur la page 1sur 7

P.O.O.

en C++
TP 2 : Initiation à la bibliothèque Master 1 INS3I
standard du C++ P.-A. Hébert
2021–2022

Consignes
Dans ce TP, vous allez découvrir quelques classes (et « patrons de classe ») de la
bibliothèque standard.

1 Les chaînes de caractères


1.1 Présentation de la classe string
En C, les chaînes de caractères sont définies comme de simples tableaux d’entiers (8 bits,
donc de type char). Par conséquent leur usage n’est pas très pratique. Par exemple, il faut
faire attention à ne pas accéder à des éléments hors du tableau. Il faut aussi veiller à l’inser-
tion d’un caractère de code (ASCII) 0 après le dernier caractère de la chaîne. Et surtout, cela
nécessite de gérer soi-même la mémoire allouée à la chaîne. Par exemple, une concaténation
implique forcément un allongement de la chaîne, et donc de la mémoire supplémentaire, ce
qui nécessite l’allocation d’un nouveau tableau.

En C++, cette représentation reste valable, mais elle est largement délaissée au profit de la
classe string. Un objet de cette classe représente une chaîne de caractères. L’interface de
cette classe propose une grande quantité de méthodes et fonctions. Grâce à elles, l’utilisa-
teur n’a plus à prendre en charge la gestion mémoire de ses caractères, ni même à indicer
directement les élements du tableau sous-jacent (ce qui s’accompagne forcément d’un risque
d’erreur).

L’interface de cette classe est présentée sur le site cppreference.com : c’est ici que vous
prendrez connaissance des fonctions destinées à manipuler les objets string (comme de tous
les composants de la bibliothèque standard), ainsi que de la manière de les utiliser.

1.2 Exercices
Q 1. Créez un objet de la classe std::string, avec l’instruction :

std::string chaine;

puis affichez-le en l’envoyant directement sur le flux std::cout.


Les flux et les string font tous deux partie de la bibliothèque standard (STL), et ils sont
compatibles (plus exactement, les opérateurs des flux ont été surchargés pour accepter les
objets de classe string).

Affichez ensuite sa taille (grâce à la méthode length ou size, qui retournent toutes les deux
cette information).

Enfin, quelle est la chaîne de caractères décrite par cet objet apparemment « non-initialisé » ?
Q 2. Écrire une fonction adresseMail qui prend le nom et le prénom d’un étudiant, et
renvoie une chaîne de caractères représentant son email, définie sur le modèle suivant :

<nom>.<prenom>@etu.univ-littoral.fr

Vous pourrez utiliser les opérateurs = et + (qui sont surchargés pour la classe string), ou
toute autre méthode ou fonction décrite dans la documentation de la STL.

Récupérez ensuite les nom et prénom d’un étudiant au clavier, dans deux objets std::string.
Puis affichez son email en utilisant la fonction précédemment définie.

Q 3. Demandez une chaîne de caractères chaine1 au clavier.

Puis demandez une autre chaîne de caractères chaine2, et répétez cette demande tant que
chaine2 est « plus petite » que chaine1, suivant l’ordre alphabétique.

Les objets string peuvent en effet être comparés grâce aux opérateurs <, > et ==.

Q 4. Écrivez une fonction qui affiche tous les caractères d’une chaîne, en les séparant par
un saut de ligne.

Pour accéder aux caractères de la chaîne, vous aurez le choix entre l’opérateur [ ] et la
méthode at. La méthode at a l’avantage d’effectuer une vérification de l’indice : son débor-
dement provoque une erreur à l’exécution (erreur qui peut être détournée).

Q 5. Écrivez une fonction qui supprime toutes les lettres e d’un objet string. Dans cette
première version, l’argument est transmis par valeur.

Vous pourrez utiliser la méthode push_back pour composer la chaîne résultat à retourner.

Q 6. Même question que précédemment, mais l’argument est maintenant transmis par réfé-
rence. Travaillez uniquement sur l’objet transmis, en utilisant cette fois-ci les fonctions at,
erase et length.

2 Les nombres complexes


2.1 Présentation du patron de classe std::complex
Les nombres complexes ne font pas partie du langage C++. Pour rappel, les types élémen-
taires destinés à gérer des nombres comprennent uniquement les entiers (char, int) et les
nombres à virgule flottante (float et double).

Cette limitation n’est pas gênante, car la bibliothèque standard (STL) fournit une classe
spécifique : std::complex.

2
Plus exactement, il ne s’agit pas d’une classe, mais d’un patron de classe, c’est-à-dire d’un
modèle générique de classes, fonction d’un (ou plusieurs) type(s). En l’occurrence, le patron
std::complex dépend d’un type T « nombre réel ». Ce type détermine le format de stockage
des parties réelles et imaginaires du complexe.

Suivant la précision souhaitée, on générera une classe « nombre complexe » en paramétrant


le patron std::complex avec l’un des 3 types C++ représentant les nombres réels. On
obtiendra donc, par ordre de précision croissante, l’une des classes suivantes :
— std::complex<float>
— std::complex<double>
— std::complex<long double>.

L’interface de ce patron de classe est présentée sur le site cppreference.com. Vous y trouve-
rez l’ensemble des méthodes et fonctions qui permettent de manipuler ces nombres complexes.

Ces fonctions dépendent logiquement du type T choisi, souvent même au niveau de leurs
arguments, ou de leur valeur de retour.

Parmi ces fonctions, vous trouverez des opérateurs (+, -, *, . . .), qui permettent d’adapter
les opérations arithmétiques usuelles aux nombres complexes.

3
2.2 Exercices
Q 1. Analysez le code suivant, en justifiant toutes les fonctions et opérations appliquez aux
objets std::complex<float>.

#include <complex>
#include <iostream>

using namespace std;

int main()
{
cout << "Un premier complexe : " << endl;
complex<float> a(3.0);
cout << "a : " << a << endl << endl;

cout << "Un deuxieme complexe : " << endl;


complex<float> b(1, 3);
cout << "b : " << b << endl << endl;

cout << "Ses deux accesseurs : " << endl;


cout << "b.real() : " << b.real() << endl;
cout << "b.imag() : " << b.imag() << endl << endl;

cout << "Quelques methodes : " << endl;


b.operator+=(a);
cout << "b : " << b << endl;
b += a;
cout << "b : " << b << endl;
b = a;
cout << "b : " << b << endl << endl;

cout << "Quelques fonctions : " << endl;


cout << "operator+(b, float(1)) : " << operator+(b, float(1)) << endl;
cout << "b + b : " << b + b << endl;
cout << "b : " << b << endl << endl;

return 0;
}

Listing 1 : complex.cpp

Q 2. Écrivez une fonction qui retourne le n-ième (n ∈ N) terme de la suite complexe :


2
z0 = 0; zn = zn−1 + c.

Votre fonction prend donc 2 paramètres : l’indice n du terme souhaité, et le paramètre c de


la suite (un complexe).

4
3 Les conteneurs
3.1 La classe vector de la STL
De même qu’on ne représente pas en C++ les chaînes de caractères à l’aide d’un tableau
de caractères (on préfère la classe std::string), les ensembles - de taille variable - d’objets
de même classe ne sont pas manipulés directement à partir de tableaux, mais à l’aide de
conteneurs.

Parmi les conteneurs de la STL, ce sont les std::vector qui servent à représenter des ta-
bleaux, gérés dynamiquement. Leur taille n’a donc pas besoin d’être connue à la compilation,
et surtout, elle peut évoluer au gré du programme. Comme std::complex, c’est aussi un
patron de classe, dont le type-paramètre sert à spécifier la nature des éléments stockés. Mais
au contraire du patron de classe std::complex, on peut générer des vecteurs de n’importe
quel type d’objets : int, std::string, Etudiant, etc.

Parmi ses nombreuses méthodes (cf. la documentation officielle de la STL), vous pourrez
notamment utiliser :
— son constructeur (fonction d’initialisation) : qui attend simplement en argument le
nombre d’éléments à placer dans l’objet ;
— size qui permet de récupérer ce nombre d’éléments ;
— at et operator[] : vous connaissez déjà ces fonctions, elles s’utilisent de la même
manière que pour la classe string.

L’un des principaux avantages de ces classes conteneurs, est la gestion dynamique des élé-
ments contenus : elle est automatique (comme dans std::string, qui, dans une certaine
mesure, se comporte de manière assez similaire à un vector<char>).

5
3.2 Application

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

void affiche_for(const vector<string> &v)


{
for (string s: v)
cout << s << " ";
}

void affiche_iterator(const vector<string> &v)


{
vector<string>::const_iterator i;
for (i = v.begin(); i != v.end(); i++)
cout << *i << " "; //l'iterator se comporte comme un pointeur
}

void affiche_lambda(const vector<string> &v)


{
// fonction "for_each" definie dans la librairie "algorithm"
for_each( v.begin(), v.end(), [](string s){ cout << s << " "; } );
}

int main()
{
vector<string> tab = {"un", "deux", "trois"};

cout << "Le vector tab contient " << tab.size() << " elements." << endl;

affiche_for(tab);
cout << endl << endl;

affiche_iterator(tab);
cout << endl << endl;

affiche_lambda(tab);
cout << endl << endl;
}

Listing 2 : vector.cpp

Q 1. Analysez la 1ère fonction du fichier 2.


Depuis la norme C++11, il existe une syntaxe du for adaptée aux conteneurs, et plus généra-
lement à l’ensemble des classes proposant des itérateurs begin et end (cf. question suivante).

Analysez ensuite la 2ième fonction de ce même fichier.


Les itérateurs sont des objets utilisés pour parcourir un ensemble ordonné d’éléments (un
« conteneur »). Ils se comportent globalement comme des pointeurs (notamment grâce aux
opérations d’incrémentation (++) et de déréférencement (*)).

La 3ième fonction exploite la notion de « fonction lambda » du C++11 : il s’agit d’une


6
fonction non-nommée (anonyme) qui peut être créée à la volée, dans un bloc d’instructions.

Q 2. Écrire une fonction qui place dans un tableau dynamique fourni en paramètre, n
nombres complexes uniformément espacés dans l’intervalle [0,j]. Le premier sera donc 0, le
dernier j, et l’écart entre deux valeurs consécutives sera toujours le même (à calculer).
La fonction prendra en paramètre un objet vector et le nombre n.

Affichez le contenu du vector à l’aide d’une fonction supplémentaire, bâtie suivant l’un des
exemples précédents.

Vous aimerez peut-être aussi