Big Tuto : Apprenez le C++

Chapitre 9 : Autres fonctionnalités du langage appliquées aux fonctions (1)

Tutoriel présenté par : Robert Gillard (Gondulzac)
Date d'écriture : 17 janvier 2016
Date de révision : -

      Préliminaires 

   Nous continuerons l'étude des diverses fonctionnalités du langage C++ en appliquant celles-ci aux fonctions. Une fonction étant un des éléments les plus importants d'un programme, nous verrons, dans la mesure du possible, comment intégrer nos nouveaux acquis dans un ou plusieurs exemples se rapportant aux fonctions. wink

 

Retrouvez les projets complets de ce chapitre :

  

 

   1 – Allocation dynamique de mémoire (partie 1)

   Jusqu'à présent, toutes les variables utilisées dans nos exemples étaient créées sur la pile. En effet, nous avons vu que dans une fonction, le domaine de définition d'une variable était la fonction elle-même et nous savons qu'une variable définie dans une fonction est enlevée de la pile au sortir de celle-ci. wink

   Le langage C++ permet la création de variables dans une autre partie de la mémoire que l'on appelle le ''tas'' (en anglais ''heap''). Il s'agit de l'allocation dynamique de mémoire, déjà exploitée par le langage C, et notamment à l'aide des fonctions malloc() et free(). Le langage C++, quant à lui utilise les mots-clé ''new'' et ''delete'' pour l'allocation et la dé-allocation d'un emplacement mémoire dans le ''tas''.

   Une variable créée dans le tas doit toujours être désallouée par ''delete'' dès qu'un programme n'en n'a plus la nécessité car il pourrait s'ensuivre des comportements imprévus de l'application voire tout simplement un crash. C'est pourquoi la nouvelle norme du C++ 11 a introduit un nouveau système d'allocation de mémoire à l'aide de pointeurs dits ''intelligents'' ou ''smart pointers'' qui permettent une dé-allocation automatique, rendant une application plus sûre, donc plus stable. Cette déallocation automatique a notamment été implémentée dans la librairie SFML que certains d'entre vous ont pu découvrir dans les Big Tutos SFML : Rabidja ou Legends of Meruvia. Nous verrons ce nouveau système d'allocation au cours de l'étude de la POO, dans un chapitre ultérieur traitant de l'Allocation dynamique de mémoire (partie 2). angel

 

       1.1 – Création de variables dans le "tas"

   Une variable du tas se crée en utilisant le mot réservé ''new''. Celui-ci renvoie un pointeur sur le début de la zone mémoire allouée à la variable.

   Exemple :

   string *chaine = new string;

   Ici, chaine est donc à la fois le nom donné à une variable de type string et un pointeur sur le premier des éléments qui composent cette chaîne. Dès que l'espace mémoire lui a été réservé, on peut y accéder directement avec l'instruction cout << chaine, ou bien accéder à chacun de ses éléments au moyen des fonctionnalités que nous avons vues jusqu'à présent soit les indices, les pointeurs ou les itérateurs.

   Un autre exemple serait de créer un tableau de char contenant un nombre connu de caractères, avec un message d'avertissement s'il n'y a pas assez de mémoire disponible :

   char *chaine = new char[500];
   if (!chaine) cerr <<  "Mémoire insuffisante !" << endl;

   Il ne faut pas oublier que toute tentative d'accéder à une variable du tas pour laquelle l'espace mémoire n'est pas disponible va provoquer, dans la majorité des cas, un blocage du PC. indecision

 

       1.2 – Destruction de variables du "tas"

   Dès qu'une variable a été créée dans le tas et qu'elle n'a plus aucune utilité dans le programme, elle sera détruite à l'aide de l'instruction delete. L'espace qui avait été réservé par l'intermédiaire de la commande new est réaffecté au tas et cet espace peut alors être ré-alloué au moyen d'un nouveau mot-clé new.

   La destruction de la variable ''chaine'' de notre premier exemple de type string s'écrira donc :

   delete chaine;

   Et nous pourrons détruire les 500 caractères réservés dans notre second exemple en écrivant :

   delete [500] chaine;

   Une erreur difficile à détecter

   Si on essaye d'utiliser l'opérateur delete pour détruire une variable du tas qui a déjà été détruite ultérieurement, cette erreur ne sera pas détectée par le compilateur et sera donc très difficile à déceler dans le programme. Une grande précaution s'impose donc lors de la création et de la destruction de variables dans le tas ; cependant celles-ci ont quand-même un certain avantage sur les variables créées sur la pile qui pourraient continuer à occuper de la place mémoire jusqu'à ce qu'un programme se termine. angel

   Nous verrons par la suite que les variables créées sur le tas sont souvent employées dans les méthodes de classes en POO. wink

   Et nous allons voir un exemple de création de variables dans le tas des deux types ''string'' et ''char'' cités plus haut dans un petit projet qui demande à l'utilisateur d'entrer un nom et un prénom au clavier.

 

//Projet 061Allocation_dynamique_1
//Création et destruction de variables dans le tas
#include <iostream>
#include <conio.h>
#include <string>
 
using namespace std;
using uint = unsigned int;
 
void Out_Of_Memory();
 
 
int main()
{
//Reservation d'une variable string dans le tas
string *nom = new string;
if (!nom)
Out_Of_Memory();
 
cout << endl;
cout << "Entrez votre nom : ";
cin >> *nom;
 
//Reservation d'une variable de type char dans le tas
char *prenom = new char[20];
if (!prenom)
Out_Of_Memory();
 
cout << "Entrez votre prenom : ";
cin >> prenom;
 
cout << endl;
cout << "Vous vous appelez " << prenom << " " << *nom << endl;
 
//Dé-allocation de la mémoire
delete nom;
delete[20]prenom;
 
_getch();
return 0;
}
 
 
void Out_Of_Memory()
{
cerr << "Memoire insuffisante" << endl;
}

 

   Dans la fonction main(), nous réservons nos variables nom et prenom. Vous voyez que la saisie des nom et prenom se fait en utilisant : le pointeur déréférencé dans le premier cas, et le nom du tableau dans le second cas. wink


   Dans le premier cas, new renvoie un pointeur sur la chaîne ''nom''. Pour ce qui est du tableau de type char, new renvoie un pointeur sur le premier élément du tableau, ce qui veut dire que ''prenom'' est à la fois le nom donné au tableau réservé dans le tas et un pointeur sur le premier élément du tableau. cheeky


   Et pour terminer, nous n'oublions pas de désallouer la mémoire réservée pour nos deux variables. wink

   C'est comment ton nom ? laugh

   Humm... !? blush

 

       1.3 – Création et destruction de références dans le "tas"

   Nous pouvons aussi créer des variables d'adresses (références) dans le tas. Leur gestion est quelque peu, plus lourde qu'avec les pointeurs mais nous devons quand-même en faire mention.

   Exemple :

   Création d'une référence de type int dans le tas : int &nombre = *new int; 

   Attention : dans ce cas l'opérateur d'indirection '*' doit se trouver avant newwink

   Et pour la dé-allocation mémoire de la variable, delete a besoin de connaître l'adresse du premier byte de la zone à effacer, ce qui veut dire que nous devons forcer la variable d'adresse à être considérée comme un pointeur. Comment...? frown


   En faisant un ''cast'' sur la référence, et pour notre variable nombre ci-dessus nous écrirons : delete (int*)(&nombre);

   Créer des pointeurs ou des références dans le tas ? 
   La création de pointeurs dans le tas semble la plus communément admise mais aussi un peu plus simple à utiliser. Que vous choisissiez l'une ou l'autre solution, sachez que le code généré par le compilateur sera identique pour chaque technique utilisée.

   Allez, un petit exercice ! (Exercice 1)

   Dans la fonction main(), déclarez un pointeur sur un tableau de 20 entiers à créer dans le tas.
   Vous passerez ce tableau (vide) en paramètre à une fonction ''Remplir_tableau()'' dans laquelle vous le remplirez avec 20 entiers (0 – 19).

   Repassez ensuite ce tableau à la fonction main() qui devra se charger de l'afficher à l'écran. N'oubliez pas la dé-allocation du tableau avant de sortir de la fonction main()indecision

   Si vous voulez quelque peu corser l'exercice, déclarez premièrement une variable entière en référence dans le tas, donnez-lui ensuite la valeur 20 et placez le nom de cette variable à l'intérieur des crochets de votre tableau. Mais oui, c'est facile... angry Le corrigé de l'exercice sera proposé dans le chapitre 10 et vous devriez obtenir ce genre de résultat dans la console laugh :

 

   2 – Vectors et fonctions

         2.1 – Lecture d'un Vector dans une fonction

   Dans le chapitre 3, nous avons longuement parlé des vectors. Si vous avez oublié quelques notions les concernant, je vous conseille de revoir cette partie du chapitre 3 avant de continuer notre étude sur les vectors et les fonctions. wink

   Bien, maintenant que vous vous rappelez comment créer un vector, nous allons voir comment en passer un en paramètre à une fonction afin de le lire. cool

//Projet 062Vector_Func1
//Passage d'un vector à une fonction
#include <iostream>
#include <conio.h>
#include <vector>
 
using namespace std;
using uint = unsigned int;
 
void LireVector(const vector<uint> &);
 
 
int main()
{
// Création d'un vector (vide) unsigned int dans la fonction main()
vector<uint> UnVector;
cout << endl;
 
// Remplissage du vector avec 10 nombres
cout << "Creation d'un vector de 10 uint dans la fonction main()" << endl << endl;
for (decltype (UnVector.size()) i = 0; i != 10; ++i)
UnVector.push_back(i);
 
LireVector(UnVector);
cout << endl;
 
_getch();
return 0;
}
 
 
void LireVector(const vector<uint> &MonVector)
{
cout << "Lecture du vector dans la fonction LireVector(const vector<uint> &)" << endl;
 
for (auto i : MonVector)
{
cout << i;
cout << " ";
}
cout << endl;
cout << "Longueur du vector = " << MonVector.size();
}

 

   Dans la fonction main(), nous créons un vector dénommé UnVector que nous remplissons avec 10 valeurs unsigned int.

   Une fonction LireVector() est déclarée en début de programme. Cette fonction n'ayant pour autre but que la lecture d'un vector, nous allons lui passer une référence sur un vector constant en paramètre. wink

   Dans la définition de la fonction LireVector(), nous utilisons la forme concise de la boucle for recommandée par la nouvelle norme du C++ 11, afin de lire tous les éléments du vector. Pour terminer nous affichons la longueur du vector.

   Ce qui affiche dans la console : 

 

   Il n'y a donc rien de difficile dans ce programme et nous allons maintenant créer une autre fonction pour modifier ce vector.

 

       2.2 – Modification d'un Vector dans une fonction

   Je crois que j'ai voulu dire que... frown vous allez créer une fonction qui modifie un vector initialisé dans la fonction main(). Allez hop, au boulot ! laugh

   Exercice 2 :

   Reprenez le programme ci-dessus et ajoutez-y une fonction que vous nommerez ModifyVector() ou ce que vous voudrez. indecision
   Dans cette fonction, vous ajouterez les valeurs 0, 1, 2, 3 dans le vector existant et vous le réafficherez dans la fonction main() en donnant sa nouvelle longueur.

   Vous devriez avoir quelque chose de ce genre dans la console wink :

 

       2.3 – Remplacer un tableau ou un pointeur par un Vector dans une fonction

   Considérons le corrigé de l'exercice du chapitre 7 : Exercice 3.1 dans le chapitre 8. Nous avons utilisé un tableau qui emmagasinait toutes les positions du caractère 'o' dans la chaîne ''Nabuchodonosor''.
   Cela étant dit, si nous avons un long texte à vérifier et que nous ne connaissons pas, a priori, le nombre de tel ou tel caractère pouvant se retrouver dans cette chaîne, ce n'est pas une bonne idée d'utiliser un tableau. Nous avons également fait cet exercice retournant un pointeur sur un tableau mais cette fois, pour éviter tout tableau ou pointeur, nous utiliserons une référence sur un vectorwink

   Nous afficherons le projet en premier et donnerons ensuite les explications nécessaires.

//Projet 063VectorFunc_2
//Une fonction qui prend une référence sur un vector en paramètre
#include <iostream>
#include <conio.h>
#include <string>
#include <vector>
 
using namespace std;
using uint = unsigned int;
 
// Déclaration de la fonction Recherche_char()
 
string::size_type Recherche_char(const string &chaine, const char c, string::size_type &nb_occurrences, vector<uint> &);
 
int main()
{
// Création d'un vector unsigned int (vide) dans la fonction main()
vector<uint> unVector;
 
//La chaine sur laquelle se porte la recherche
const string chaine{ "Nabuchodonosor roi de Babylone" };
 
//Le caractère à rechercher
const char c = 'o';
 
//Le nb d'occurrences de 'o' dans "Nabuchodonosor roi de Babylone"
string::size_type compteur(0);
 
Recherche_char(chaine, c, compteur, unVector);
cout << endl;
cout << "Il y a " << compteur << " 'o' dans " << chaine << endl;
//front() est le premier élément d'un vector
cout << "Le premier 'o' se trouve en position " << unVector.front() << endl << endl;
 
cout << "Les positions sont placees dans un vector" << endl << endl;
for (auto i : unVector)
cout << "Un caractere " << c << " se trouve en position " << i << " dans " << chaine << endl;
 
_getch();
return 0;
}
 
 
string::size_type Recherche_char(const string &chaine, const char caract, string::size_type &nb_occurrences, vector<uint> &monVector)
{
for (decltype(chaine.size()) i = 0; i != chaine.size(); ++i)
{
if (chaine[i] == caract)
{
monVector.push_back(i);
++nb_occurrences;
}
}
return 0;
}

 

   Donnons quelques explications : nous déclarons notre fonction Recherche_char() en lui passant non plus un tableau ni un pointeur en paramètre mais bien une référence d'unsigned int sur un vector.

   Dans la fonction main(), nous déclarons un vector (vide) d'unsigned int dénommé ''unVector''. Comme celui-ci est appelé à grandir, puisque c'est lui qui devra contenir toutes les positions du caractère 'o' de notre chaîne quelque peu allongée "Nabuchodonosor roi de Babylone", nous ne pouvons pas lui donner le qualificateur ''const''. Nous appelons ensuite la Fonction Recherche_char() en lui passant en outre le vector ''unVector'' en paramètre.

   Dans la définition de la fonction Recherche_char(), nous passons une référence vector<uint> &monVector en paramètre. Nous parcourons ensuite la chaîne jusqu'à chaine.size() - 1 et chaque fois qu'un caractère 'o' est trouvé, il est placé dans notre vector à l'aide de l'instruction push_back(). Et nous savons que nb_occurrences est la variable qui contiendra le nombre de caractères 'o' de la chaîne.

   De retour dans main(), on affiche en premier le nombre d'occurrences de 'o' dans la chaîne et ensuite la première position de ce caractère dans ''Nabuchodonosor roi de Babylone''. Depuis le chapitre 3 (Opérations sur les vectors), nous savons que front() est une méthode de la classe ''vector'' qui accède au premier élément d'un vector. Le premier élément de notre vector est donc bien ''unVector.front()''.

   Nous n'avons plus ensuite qu'à parcourir tous les éléments du vector pour afficher toutes les positions du caractère 'o' dans la chaîne. wink

   Ce qui donne dans la console : 

 

   L'utilisation d'un vector dans un programme comme celui-ci nous procure un avantage certain par rapport au retour d'un tableau ou d'un pointeur vers la fonction principale. Les pointeurs sont toujours à manier avec précaution et, d'autre part, il ne serait pas très judicieux de retourner un tableau contenant des centaines de caractères ou chaînes de type stringblush

 

        2.4 – Utiliser un tableau pour initialiser un Vector

   Nous savons que nous pouvons copier un vector dans un autre, pour autant que ceux-ci soient de même type. En effet, si v1 et v2 sont deux vectors, nous pouvons soit copier v1 dans v2 soit v2 dans v1 en écrivant :

   v1 = v2 ou v2 = v1.

   D'un autre côté, si nous nous référons à la théorie des tableaux, nous ne pouvons pas initialiser un tableau en tant que copie d'un autre tableau, ni assigner un tableau à un autre tableau. Donc si tab1[] et tab2[] représentent deux tableaux (même s'ils sont de mêmes types), nous ne pourrons pas faire :

   tab1 = tab2 ou tab2 = tab1 // C'est impossible !! angry

   Le C++ nous permet, néanmoins, d'initialiser un vector à l'aide d'un tableau. Pour ce faire, nous devrons spécifier l'adresse du premier élément ainsi que l'adresse du dernier + 1 élément du tableau que nous voulons copier (cela vous rappelle quelque chose, je crois ? wink)

   En effet, en utilisant les méthodes begin() et end() de la Standard Library, nous pourrions faire, en supposant que tab[] soit un tableau de type uint contenant 5 nombres :

   uint tab[] = { 0, 1, 2, 3, 4 };

   vector<uint> monVector( begin(tab), end(tab));

   Et ''monVector'' contiendra ainsi les 5 éléments du tableau tab[] compris entre la première et la dernière position.

   Un simple exemple :

//Projet 063VectorFunc_4
//Initialisation d'un vector par un tableau
#include <iostream>
#include <conio.h>
#include <vector>
#include <string>
 
using namespace std;
using uint = unsigned int;
 
void LireVector(const vector<string> &);
 
 
int main()
{
cout << "Initialisation d'un vector avec un tableau" << endl << endl;
// Un tableau de chaînes contenant 4 ennemis
string enemy[] = { "Gobelin", "Orc", "Elfe noir", "Sorcier" };
//Initialisation d'un vector avec le tableau
vector<string>mesEnnemis(begin(enemy), end(enemy));
 
LireVector(mesEnnemis);
cout << endl;
 
_getch();
return 0;
}
 
 
void LireVector(const vector<string> &monVector)
{
cout << "Lecture du vector dans la fonction LireVector()" << endl;
for (auto it : monVector)
{
cout << it;
cout << " ";
}
cout << endl;
cout << "Longueur du vector = " << monVector.size();
}

 

   Voilà, tout a déjà été expliqué avant le code de ce projet. wink Ce qui donne dans la console :

 

   Un petit challenge (Exercice 3)

   Modifiez le projet 062VectorFunc_1 de ce chapitre :

Dans la fonction main()

- Déclarez une constante unsigned int et donnez lui la valeur 10.
- Déclarez et initialisez un tableau d'entiers non-signés avec les valeurs (0 – 9). Donnez-lui le nom de la constante précédente comme grandeur.
- A l'aide de ce tableau, initialisez un vector (v1).
- Copiez le vector v1 dans un vector vide (v2).
- Appelez une fonction ModifyVector() et passez-lui une référence sur v2 en paramètre.
- Appelez la fonction LireVector(vector<uint> &v2)

Dans la fonction ModifyVector()

- Dans cette fonction, complétez le vector v2 avec des unsigned int (10 – 19).

Dans la fonction LireVector()

- Affichez tous les éléments de v2 ainsi que sa longueur.

   Et vous devriez avoir un résultat de ce genre dans la console :  

   Auriez-vous pu modifier v1 ? frown Justifiez votre réponse ! indecision laugh

   3 – Corrigés des exercices du chapitre 8

         Exercice 1 :

   Dans cet exercice, il vous était demandé de retourner le produit des valeurs décimales des premier et dernier caractères de la chaine "Il etait une fois... Roswyn !?", c'est-à-dire le produit de la valeur décimale du caractère ''I'' par la valeur décimale du caractère ''?''. wink

   Vous deviez obtenir ces valeurs décimales à partir des pointeurs *ptr1 et *ptr2 dans la fonction et, à titre d'information, je vous avais donné la valeur de ce produit : 4599.

   Voilà le code : 

//ExerciceChap8_1
//Calcul du produit des valeurs décimales de 2 caractères dans une chaîne
#include <iostream>
#include <conio.h>
 
using std::cout;
using std::endl;
using uint = unsigned int;
 
// Déclaration de la fonction Produit_valeurs_decimales()
uint Produit_valeurs_decimales(const char *ptr1, const char *ptr2);
 
 
int main()
{
const char chaine[] { "Il etait une fois... Roswyn !?" };
 
cout << " Une fonction qui calcule le produit des valeurs decimales" << endl;
cout << " des premier et dernier caracteres de la chaine :" << endl << endl;;
cout << " Il etait une fois... Roswyn !?" << endl << endl;
cout << " Avec passage de deux pointeurs dans la fonction" << endl << endl;
 
const char *ptr1 = nullptr;
const char *ptr2 = nullptr;
 
//On fait pointer les deux pointeurs sur le début de la chaîne
ptr1 = ptr2 = chaine;
//On appelle la fonction
auto produit = Produit_valeurs_decimales(ptr1, ptr2);
cout << "Le produit des valeurs decimales des 2 caracteres est " << produit << endl;
 
_getch();
return 0;
}
 
 
uint Produit_valeurs_decimales(const char *ptr1, const char *ptr2)
{
uint premier(0), dernier(0);
//On obtient la valeur de "premier" à partir du pointeur *ptr1
premier = *ptr1;
 
cout << "Valeur decimale du caractere I = " << premier << endl;
 
while (*ptr2)
*ptr2++;
//On repositionne le pointeur sur le caractère "?"
*ptr2--;
 
//On obtient la valeur de "dernier" à partir du pointeur *ptr2
dernier = *ptr2;
 
cout << "Valeur decimale du caractere ? = " << dernier << endl;
cout << endl;
 
return premier * dernier;
}

 

   Nous créons une fonction dénommée Produit_valeurs_decimales() à laquelle nous passons deux pointeurs sur un const char. Cette fonction doit retourner le produit des valeurs décimales des deux caractères ''I'' et ''?''. Voyons comment. wink

   Dans l'implémentation de la fonction, nous créons deux variables unsigned int ''premier'' et ''dernier'' que nous initialisons à 0. Dans main(), nous avons fait pointer ptr1 et ptr2 sur le début de la chaîne donc sur le caractère ''I''.


   Dans notre fonction, ''premier'' va prendre la valeur décimale du caractère ''I'', soit 73 (I =char(73)).


   Nous obtiendrons la valeur décimale du caractère ''?'' par un procédé déjà utilisé, en incrémentant le pointeur ptr2 jusqu'en fin de chaîne. Nous donnons alors à la variable ''dernier'' la valeur décimale du caractère pointé par ptr2, soit 63 (? = char(63)).

   Pour terminer, nous retournons le produit des deux valeurs (73 x 63 = 4599) dans la variable ''produit'' de notre fonction main() que nous n'avons plus qu'à afficher. cheeky

   Voici ce que cet exercice affiche dans la console :

 

      Exercice 2 :

   Il fallait modifier le projet 057Fonctions20 afin de retourner toutes les valeurs négatives du tableau dans la fonction principale.

   Voici le code :

//Projet ExerciceChap8_2
//Une fonction qui retourne tous les éléments négatifs d'un tableau d'entiers
//Utilisation des fonctions begin() et end() de la Standard Library
#include <iostream>
#include <conio.h>
 
using namespace std;
using std::cout;
using std::endl;
 
// Déclaration de la fonction LisTab()
int LisTab(int *debut, int *fin, int neg[]);
 
 
int main()
{
int tab[] = { 3, -2, -9, 6, 12, 21, -17, -11, 7, 1 };
//Un tableau qui recevra les valeurs négatives
int neg[10];
 
cout << "Une fonction qui retourne les valeurs negatives d'un tableau d'entiers" << endl;
cout << "Utilisation des fonctions begin() et end() de la Standard Library" << endl << endl;
 
//*debut est un pointeur sur le début du tableau d'entiers
int *debut = begin(tab);
//*fin est un pointeur sur l'élément suivant la fin du tableau d'entiers
int *fin = end(tab);
 
auto compteur = LisTab(debut, fin, neg);
cout << "Valeurs negatives du tableau :" << endl << endl;
 
for (decltype(compteur) i(0); i != compteur; ++i)
cout << neg[i] << endl;
 
_getch();
return 0;
}
 
 
int LisTab(int *ptrdebut, int *ptrfin, int neg[])
{
auto ctr(0);
 
while (ptrdebut != ptrfin)
{
if (*ptrdebut < 0)
{
neg[ctr] = *ptrdebut;
ctr++;
}
ptrdebut++;
}
 
return ctr;
}

 

   Bien, nous n'avons pas utilisé un vector comme dans l'exemple 063VectorFunc_2 puisque nous n'en avions pas encore parlé dans le chapitre précédent. wink A la place, nous passons un tableau neg[] à la fonction LisTab(), tableau qui sera destiné à acquérir les valeurs négatives de notre liste d'entiers du tableau tab[].

   Dans la fonction main(), on initialise deux pointeurs : le pointeur *debut pointant sur la première position des éléments de notre tableau d'entiers et le pointeur *fin pointant sur la dernière + 1 position du tableau. La fonction LisTab() est appelée en lui passant les des deux pointeurs *debut et *fin ainsi qu'un pointeur sur le premier élément du tableau neg[].

   Dans la fonction LisTab(), on initialise à zéro une variable ''ctr'' pour l'incrémentation du nombre de valeurs négatives que nous rencontrerons dans le tableau tab[]. La manière de procéder est maintenant connue, le pointeur ptrdebut parcourt tout le tableau et si un emplacement mémoire pointé par ptrdebut contient une valeur négative, celle-ci est ajoutée au tableau neg[].

   Pour obtenir le nombre de valeurs négatives, ctr est retourné dans la variable compteur de la fonction main(). Une boucle for parcourt alors le tableau neg[] de 0 à compteur – 1 afin d'afficher toutes les valeurs négatives. cool

   Nous obtenons comme résultat dans la console : 

   Et si vous le désirez, vous pourrez refaire cet exercice en utilisant une référence sur un vector d'entiers dans le style de l'exemple 063VectorFunc_2.


       Correction de notre petit challenge (exercice 3)

   Dans cet exercice, il vous étatit demandé, en utilisant la fonction ''isspace()'' du tableau des fonctions''cctype'' du chapitre 5, d'écrire une fonction qui transforme tous les espaces de la chaîne ''Katana tranchant du Samourai du Pont des Etoiles'' par un caractère de soulignement. Ce caractère est char(95). La nouvelle chaîne sera renvoyée et affichée dans la fonction main().

 

   Exercice 3.1 : on utilise un itérateur dans la fonction.

   Le code :  

//Projet ExerciceChap8_3
//Une fonction qui transforme les espaces d'une chaîne en caractères de soulignement
//Utilisation d'un iterateur de type string::const_iterator
#include <iostream>
#include <string>
#include <cctype>
 
using namespace std;
 
string Transforme_char(string chaine, string::const_iterator it);
 
int main()
{
string arme{ "Katana tranchant du Samourai du Pont des Etoiles" };
string::const_iterator iter;
 
cout << endl;
cout << "Chaine d'origine = 'Katana tranchant du Samourai du Pont des Etoiles'" << endl << endl;
arme = Transforme_char(arme, iter);
//On affiche la chaîne transformée
cout << arme << endl;
 
cin.get();
return 0;
}
 
 
string Transforme_char(string chaine, string::const_iterator it)
{
cout << "On utilise la fonction 'isspace()' de la librairie cctype" << endl;
cout << "et on parcourt la chaine a l'aide d'un iterateur" << endl;
cout << "pour remplacer les espaces par des caracteres de soulignement" << endl;
 
//Si on rencontre un espace on le remplace par un caractère de soulignement
for (auto it = chaine.begin(); it != chaine.end(); ++it)
if (isspace(*it))
*it = char(95);
 
cout << endl;
return chaine;
}

 

    La fonction Transforme_char() nous intéresse particulièrement. Cette fonction prend une chaîne de caractères et un const_iterator en arguments. On utilise à nouveau les méthodes begin() et end() de la Standard Library qui déterminent les première et dernière + 1 positions de la chaîne. Dès que l'itérateur se porte sur un emplacement qui contient un espace, le contenu de cet cet emplacement est aussitôt remplacé par un caractère de soulignement (''_'' = char (95)).


   Et nous rappelons à tout hasard qu'un itérateur agissant comme un pointeur, il doit être déréférencé. wink

   La chaîne est ensuite retournée dans la variable ''arme'' de la fonction main() et affichée à la ligne suivante.

   Vous devriez avoir un résultat de genre dans la console : 

 

   Exercice 3.2 : on utilise un indice de 0 à chaine.size() - 1.

   Le code : 

//Projet ExerciceChap8_3_2
//Une fonction qui transforme les espaces d'une chaîne en caractères de soulignement
//Utilisation d'un indice de 0 à chaine.size() - 1
#include <iostream>
#include <string>
#include <cctype>
 
using namespace std;
 
string Transforme_char(string chaine);
 
int main()
{
string arme{ "Katana tranchant du Samourai du Pont des Etoiles" };
 
cout << endl;
cout << "Chaine d'origine = 'Katana tranchant du Samourai du Pont des Etoiles'" << endl << endl;
arme = Transforme_char(arme);
//On affiche la chaîne transformée
cout << arme << endl;
 
cin.get();
return 0;
}
 
 
string Transforme_char(string chaine)
{
cout << "On utilise la fonction 'isspace()' de la librairie cctype" << endl;
cout << "et on parcourt la chaine a l'aide d'un indice" << endl;
cout << "pour remplacer les espaces par des caracteres de soulignement" << endl;
 
//Si on rencontre un espace on le remplace par un caractère de soulignement
for (decltype(chaine.size()) i = 0; i != chaine.size(); ++i)
if (isspace(chaine[i]))
chaine[i] = char(95);
 
cout << endl;
return chaine;
}

 

   Comparons la fonction Transforme_char() avec celle de l'exercice précédent. Cette fonction ne prend qu'une variable de type string en argument. On parcourt la chaîne de 0 à chaine.size() - 1 à l'aide d'un indice ''i'' dont le type est extrait par le spécificateur de type ''decltype''.


   A chaque fois que chaine[i] se trouve être un caractère ''espace'', il est aussitôt remplacé par un caractère de soulignement (''_'' = char (95)).

   Le retour dans le main() se fait par l'intermédiaire de la variable arme comme dans l'exercice précédent. wink

   Vous devriez avoir un résultat de ce genre dans la console :

 

   Vous voyez que pour arriver à un même résultat, il y a plusieurs manières de programmer ce genre de fonctions. wink Mais quelle est la meilleure façon de faire, alors ? surprise Pour des boucles relativement courtes cela n'a pas vraiment d'importance mais pour des boucles plutôt longues à itérer, les pointeurs se trouvent être plus rapides que les variables indicées. cool

 

   Pour finir, je vous rappelle que les corrigés des exercices proposés dans ce chapitre seront présentés à la fin du chapitre 10. wink

   @ bientôt pour le chapitre 10 - Autres fonctionnalités du langage appliquées aux fonctions (2)

            Gondulzak.  angel
 

   

 

Connexion

CoalaWeb Traffic

Today240
Yesterday274
This week1311
This month4985
Total1744192

27/04/24