Big Tuto : Apprenez le C++

Chapitre 10 : Autres fonctionnalités du langage appliquées aux fonctions (2)

Tutoriel présenté par : Robert Gillard (Gondulzac)
Date de publication : 28 février 2016
Date de révision : -

      Préliminaires 

   Avant de passer aux entrées/sorties dans les fichiers ainsi qu'aux types de données complexes, nous allons essayer de terminer en un ou deux chapitres ce qu'il nous reste à voir avec quelques autres fonctionnalités du langage appliquées aux fonctions. Nous pourrons ainsi passer à l'étude des classes et de la POO dès les chapitres suivants mais avant, je penserai à préparer un exercice commenté reprenant plusieurs fonctionnalités que nous aurons vues jusque là. 

Retrouvez les projets complets de ce chapitre :

  

 

   1 – "constexpr" et expressions constantes (C++ 1)

Au moment où j'écris ce chapitre, le mot-clé constexpr n'est pas reconnu ( et ne le sera sans doute jamais ) par VS 2013. Il n'est compilable qu'avec VS 2015 et peut-être l'un ou l'autre compilateur que je n'ai pas testé.  

       1.1 – Définitions et exemples

   Dans le chapitre 2 ( Types de données – variables et constantes ), nous avons brièvement mentionné un mot-clé du C++ 11 ( constexpr ). Cet élément du langage désigne une expression constante.

   Une expression constante est une expression dont la valeur ne peut pas changer et qui ne sera seulement évaluée qu'à la compilation. Les nuances entre ''const'' et ''constexpr'' sont subtiles, en voici quelques-unes:

   Dans le tutoriel ''Legends of Meruvia'' de Jay (Big Tuto Action-RPG), on rencontre de nombreuses déclarations de constantes dont ce petit échantillon :

/* Taille d'une tile (32 x 32 pixels) */
const int TILE_SIZE = 32;
 
/* Constante pour l'animation */
const int TIME_BETWEEN_2_FRAMES = 20;

   Dans de telles définitions, les constantes TILE_SIZE et TIME_BETWEEN_2_FRAMES sont des expressions constantes et le qualificateur pourrait être défini de la manière suivante ( VS 2015 uniquement ) :

constexpr int TILE_SIZE = 32; // où 32 est l'expression constante
constexpr int TIME_BETWEEN_2_FRAMES = 150; // où 150 est l'expression constante

   Et dans ce cas, constexpr int est appelée une variable constexpr.


  De même, dans un nombre quelconque de fichiers par exemple égal à 25 et tel que :

const int MAX_FICH = 25;                                       MAX_FICH est une expression constante.
Dans l'expression : const int LIMITE = 25 + 1;        LIMITE est également une expression constante.         

 

       1.2 – Variables constexpr

   Sous le nouveau Standard C++ 11, on peut demander au compilateur si une variable est une expression constante en déclarant la variable ''constexpr''

constexpr int janvier = 31;                               // 31 est une expression constante.
constexpr int MAX_FICH = 25;                      // 25 est une expression constante.
constexpr int LIMITE = MAX_FICH + 1;        // MAX_FICH + 1 + 1 est une expression constante.

Une expression constante ne sera évaluée qu'à la compilation d'un programme !

   Considérons le petit exemple suivant : 

int main()
{
string alphabet = "abcdefghijklmnopqrstuvwxyz";
 
const int longueur = alphabet.size();
cout << longueur;
 
cin.get();
return 0;
}

 

   Concentrons-nous sur la ligne const int longueur = alphabet.size(). Bien que ''longueur'' soit une const int, ce n'est pas une expression constante car la valeur de la fonction size() est inconnue à la compilation et ne sera évaluée que lors du déroulement de celle-ci.

   Par contre, écrire : constexpr int longueur = 26; veut dire que 26 est une expression constante de la variable constexpr int ''longueur''.

   Voilà pour ce sujet quelque peu délicat. Et la nouvelle norme recommande d'utiliser ''constexpr'' pour initialiser des variables que l'on désire utiliser comme expressions constantes (avec VS 2015 bien entendu). wink

 

       1.3 – Pointeurs constexpr

   Une chose importante à retenir est que si l'on définit un pointeur sur une déclaration constexpr, le spécificateur constexpr s'applique au pointeur, pas au type sur lequel le pointeur pointe.

const int *ptr1 = nullptr;              // ptr1 est un pointeur sur un entier constant.
constexpr int *ptr2 = nullptr;      // ptr2 est un pointeur constant sur un entier.
 

   Dans le chapitre 4 - 1.8 (Les pointeurs et le qualificateur const), nous écrivions un pointeur constant sur un entier de la sorte :

int *const ptr2 = nullptr;            // ptr2 est un pointeur constant sur un entier.

Avec constexpr, l'écriture se fait différemment. wink

 

       1.4 – Fonctions constexpr

   Certaines fonctions peuvent être déclarées ''constexpr''. En général ce sont de courtes fonctions qui peuvent être écrites sous quelques restrictions :

- Il ne peut y avoir qu'une seule valeur retournée.
- Le type de retour doit être un littéral ou une expression donnant un littéral comme valeur. (Ex : return 17; ou return (( a > b) ? 0 : 1))
- Aucune variable ne peut-être initialisée dans une fonction constexpr.

Remarque : Depuis l'avènement de la norme C++ 14, les fonctions constexpr sont plus permissives et permettent de nouvelles fonctionnalités dans le corps de la fonction mais ceci sort, bien entendu, du cadre de notre tutoriel.

   Un exemple de fonction ''constexpr'' : Le calcul d'une factorielle

   Nous n'utiliserons pas de fonctions ''constexpr'' dans notre big tuto C++ (à moins que nous n'en ayons l'utilité dans la seconde partie cheeky) mais nous devons quand même actuellement montrer de quoi il retourne à l'aide d'un exemple. Cet exemple portera sur le calcul d'une factorielle, mais pour bien montrer les différences entre une fonction classique et la même fonction pouvant être déclarée constexpr, nous présenterons en premier une fonction classique de calcul d'une factorielle. 

    Pour rappel, la factorielle d'un nombre n est le produit des nombres de 1 à n et s'écrit n!. Par exemple, factorielle 5 est 5! et est égale à : 1 * 2 * 3 * 4 * 5 = 120.

 

//Projet 065Factor_1
//Une fonction classique de calcul d'une factorielle
 
#include <iostream>
#include <conio.h>
 
using namespace std;
using ULL = unsigned long long;
using uint = unsigned int;
 
// Fonction Factor
ULL Factor(uint nombre)
{
// Variable locale qui conservera les résultats des calculs
ULL tempVar(1);
 
while(nombre > 1)
tempVar *= nombre--;
return tempVar;
}
 
 
int main()
{
uint n(0);
 
cout << "Entrez un nombre de 0 a 10 " << endl;
cin >> n;
cout << endl;
 
cout << "Calcul de factorielle " << n << endl;
// Le résultat de la factorielle de n est renvoyé dans la variable f.
ULL f = Factor(n);
cout << n << "! = " << f << endl;
 
_getch();
return 0;
}

 

   Dans la fonction Factor(), le calcul se fait dans la boucle while. A chaque itération, la valeur de la variable passée en paramètre est décrémentée d'une unité et chaque fois multipliée à la nouvelle valeur de la variable ''tempVar''. Lorsque la variable ''nombre'' atteint la valeur 1, le résultat de la variable ''tempVar'' est retourné dans la variable ''f'' de type unsigned long long dans la fonction main() où il est affiché.

 

   Voyons maintenant le même programme avec la fonction Factor() définie ''constexpr'' ( VS 2015 uniquement ) :

//Projet 066constexprFactor_2
//Calcul d'une factorielle à l'aide d'une fonction constexpr
#include <iostream>
#include <conio.h>
 
 
using namespace std;
 
using ULL = unsigned long long;
using uint = unsigned int;
 
 
constexpr ULL Factor(uint n)
{
return ( (n == 0) ? 1 : (n * Factor(n - 1)));
}
 
 
int main()
{
uint nombre(0);
 
cout << "Entrez un nombre de 0 a 10 " << endl;
cin >> nombre;
 
cout << "Calcul de factorielle " << nombre << endl;
cout << nombre << "! = " << Factor(nombre) << endl;
 
_getch();
return 0;
}

 

   Considérons la fonction constexpr ULL Factor(uint n). Celle-ci n'initialise aucune variable et ne possède qu'une seule instruction : elle retourne une expression constante qui prendra la valeur 1 comme valeur de factorielle 0, soit une valeur différente pour tout nombre compris entre 1 et 10.
   La valeur retournée par la fonction Factor() ne sera évaluée qu'à la compilation.

   Ces deux projets donnent le même résultat dans la console :

 

   J'ajouterai pour terminer ce paragraphe, que les fonctions ''constexpr'' devraient être implémentées inline et définies dans un fichier header.

   Et nous en resterons là en ce qui concerne le mot-clé ''constexpr'', tant au niveau des variables, des pointeurs ou des fonctions constexpr. Ce tutoriel étant en principe accessible aux débutants du langage C++, nous ne voudrions pas le rendre trop difficile. indecision

 

    2 – Retour sur les passages d'arguments dans une fonction

        2.1 – Passage explicite d'une longueur de tableau   

   Quand, a priori, nous ne connaissons pas la longueur d'un tableau, si nous n'utilisons pas un vector, nous pouvons passer un tableau vide en paramètre. Soit :

type_tableau nom_tableau[];

   Il se fait que dans les programmes écrits en langage C et dans d'anciens programmes C++, il était usuel de définir un second paramètre indiquant la longueur du tableau.

   En utilisant les conventions de la Standard Library et une approche semblable au projet 057Fonctions20 du chapitre 8, nous pourrions réécrire la fonction LisTab() de la sorte :

void LisTab(const int tableau[], size_t longueur_tableau)
{
for (size_t i(0); i != longueur_tableau; ++i)
{
cout << tableau[i] << " ";
}
 
cout << endl;
}

 

   Dans cette version, nous utilisons le paramètre longueur_tableau de type size_t afin de déterminer le nombre d'éléments qui doivent être lus. wink

   Dès qu'on appelle la fonction LisTab(), nous devons passer ce paramètre additionnel. Mais en utilisant les conventions de la Standard Library, longueur_tableau peut-être transformé en end(tableau) – begin(tableau), l'appel de la fonction se fait alors comme suit :

   LisTab(tableau, end(tableau) - begin(tableau));


   Exercice 1 :

   En vous référant au projet 057Fonctions20 du chapitre 8 et à ce qui vient d'être dit ci-dessus, écrivez une fonction qui lit 10 entiers (0... 9).
   Le tableau sera initialisé dans la fonction main() et sa longueur sera de type size_t.
   Utilisez le schéma suivant :

- Déclaration de la fonction.
- Ecriture de la fonction main().
- Ecriture de la définition de la fonction.

   Vous pourriez avantageusement passer un pointeur en paramètre de la fonction à la place du tableau montré dans l'exemple. wink
 

        2.1 – Une référence en paramètre d'un tableau de dimension connue

   De même qu'un vector, nous savons qu'un tableau peut contenir des éléments de tout type. D'un autre côté, nous pouvons définir un pointeur ou une référence sur un tableau mais cette dernière demande une écriture quelque peu spéciale. Considérons les initialisations suivantes :

string *armes[10];    // armes est un tableau de 10 pointeurs sur une chaîne.

   Pour pointer sur un tableau contenant 10 armes, nous écrirons :

string (*ptrArmes) [10] = &armes;

   Les parenthèses entourant *ptrArmes sont nécessaires et indiquent que ptrArmes est un pointeur et que ce pointeur pointe sur un tableau de 10 armes de type string.
   De même, une référence sur un tableau de 10 armes de type string s'écrira :

string (&refArmes) [10] = armes;

   Bien, on fait quoi maintenant... cheeky un exemple ou un exercice ? indecision Allez, je vais quand-même donner un exemple de fonction à qui l'on passe une référence sur un tableau, sinon vous allez me dire que vous ne ferez plus d'exercices si vous n'avez pas d'exemple auparavant... laugh

 

//Projet 067ReferenceSurTableau
//Une fonction à qui l'on passe une référence sur un tableau de strings
 
#include <iostream>
#include <string>
 
using namespace std;
 
void LisTab(const string (&refArmes)[10]);
 
 
int main()
{
const string armes[] = { "Epee courte", "Epee longue", "Epee a deux mains",
"Arc court", "Arc long", "Arc composite",
"Baton", "Gourdin", "Fleau", "Hallebarde" };
 
 
const string(&refArmes)[10] = armes;
 
cout << endl;
cout << "Appel de la fonction 'LisTab()'" << endl << endl;
//On passe une référence sur le tableau en paramètre
LisTab(refArmes);
cout << endl;
 
cin.get();
return 0;
}
 
 
void LisTab(const string(&refArmes)[10])
{
//On lit toutes les chaînes du tableau armes.
for (int i = 0; i != 10; ++i)
cout << refArmes[i] << endl;
}

 

   Si avec une telle quantité d'armes on n'arrive pas à bout du troll du coin, je n'y comprends plus rien ! laugh

   Dans la fonction main(), nous définissons un tableau const string contenant 10 armes. A la ligne suivante, comme expliqué plus haut, on initialise une référence sur le tableau de ces 10 éléments.

   Nous passons ensuite cette référence à la fonction LisTab(). On ne passe pas un tableau entier à la fonction mais simplement une référence à ce tableau. La fonction LisTab() n'a plus qu'à afficher toutes les chaînes du tableau en commençant par celle située à l'adresse &refArmes[0].

   Il y a quand-même une réserve à émettre quant à ce type d'écriture. En effet, la dimension du tableau faisant partie intégrante de son type, nous devons relier cette dimension au paramètre passé lors de l'implémentation de la fonction. Dans notre cas précis, ceci limite bien entendu la capacité de notre tableau à 10 armes (même si nous n'avions pas utilisé le qualificateur const). wink
   Ceci dit, à moins que d'initialiser un vector avec le tableau, comme nous l'avons vu dans le chapitre 9. cheeky

   Ce qui donne dans la console :  

 

        2.3 – Une liste d'initialisation en paramètre d'une fonction (C++ 11)

   Dans le chapitre 6 (2.4 – Paramètres par défaut), nous avons vu que nous pouvions définir des fonctions ayant moins d'arguments que le nombre de paramètres de la déclaration d'une même fonction.


   Avec le C++ 11, nous pouvons passer une liste d'initialisation en paramètre. Une initializer_list est une library de types qui représente un tableau d'éléments de types identiques ayant tous une valeur const (les éléments de la liste ne peuvent pas être modifiés).


   Au même titre qu'un vector, une ''initilizer_list'' est de type template et nous devons spécifier le type des éléments que la liste contiendra.

   Exemple : initializer_list<string> armes;

   Nous allons voir un exemple d'utilisation d'une ''initializer_list'' contenant des éléments (armes) de type stringwink

   Et nous devrons ajouter le fichier header correspondant dans tout programme utilisant une liste d'initialisation.

#include <initializer_list>

   Nous écrirons d'abord notre projet et donnerons tous renseignements utiles par la suite. cool

 

//Projet 068Initializer_list
//Une fonction à qui l'on passe une liste d'initialisation
 
#include <iostream>
#include <string>
#include <initializer_list>
 
using namespace std;
 
 
void Affiche_arme(initializer_list<string> armes)
{
for (auto debut = armes.begin(); debut != armes.end(); ++debut)
cout << *debut << endl;
}
 
 
int main()
{
initializer_list<string> armes;
 
armes = { "Arc court", "Arc long", "Arc composite","Baton",
"Gourdin", "Fleau", "Arbalete legere", "Arbalete lourde" };
 
cout << endl;
 
//Dans la liste d'initialisation on crée deux tableaux de types d'armes différents
string armes_contondantes[] = { "Baton", "Gourdin", "Fleau d'armes" };
string armes_de_traits[] = { "Arc court", "Arc long", "Arc composite",
"Arbalete legere", "Arbalete lourde" };
 
string arme_actuelle;
int type_no(0);
int arme_no(0);
 
cout << "liste des armes :" << endl << endl;
cout << "<1> Arc court" << endl;
cout << "<2> Baton" << endl;
cout << "<3> Arc long" << endl;
cout << "<4> Arbalete legere" << endl;
cout << "<5> Gourdin" << endl;
cout << "<6> Arc composite" << endl;
cout << "<7> Fleau d'armes" << endl;
cout << "<8> Arbalete lourde" << endl << endl;
 
//On choisit une arme
cout << "Entrez un chiffre correspondant a une arme : ";
cin >> arme_no;
 
 
switch (arme_no)
{
case 1:
arme_actuelle = "Arc court";
break;
case 2:
arme_actuelle = "Baton";
break;
case 3:
arme_actuelle = "Arc long";
break;
case 4:
arme_actuelle = "Arbalete legere";
break;
case 5:
arme_actuelle = "Gourdin";
break;
case 6:
arme_actuelle = "Arc composite";
break;
case 7:
arme_actuelle = "Fleau d'armes";
break;
case 8:
arme_actuelle = "Arbalete lourde";
break;
default:
break;
}
 
cout << endl;
cout << "<1> Armes de traits" << endl;
cout << "<2> Armes contondantes" << endl << endl;
 
//On choisit un type d'arme
cout << "Entrez un type d'armes : ";
 
cin >> type_no;
cout << endl;
 
switch (type_no)
{
case 1:
 
//Si l'arme choisie est une arme de traits on n'affiche que celles-ci de la
//liste
if (armes_de_traits[0] == arme_actuelle || armes_de_traits[1] == arme_actuelle
|| armes_de_traits[2] == arme_actuelle || armes_de_traits[3] == arme_actuelle
|| armes_de_traits[4] == arme_actuelle)
Affiche_arme({ "Armes de trait : ", " ", "Arc court", "Arc long",
"Arc composite", "Arbalete legere", "Arbalete lourde" });
else
Affiche_arme({ "Erreur ! ", arme_actuelle, "pas dans la liste des
armes de traits" });
cin.get();
break;
 
case 2:
 
//Si l'arme choisie est une arme contondante on n'affiche que celles-ci de la
//liste
 
if (armes_contondantes[0] == arme_actuelle || armes_contondantes[1] ==
arme_actuelle || armes_contondantes[2] == arme_actuelle)
Affiche_arme({ "Armes contondantes : ", " ", "Baton", "Gourdin", "Fleau
d'armes" });
else
//Les apostrophes à l'intérieur des parenthèses sont obligatoires !
Affiche_arme({ "Erreur ! ",arme_actuelle, "pas dans la liste des
armes contondantes" });
cin.get();
 
break;
}
 
cin.get();
return 0;
}

 

   Voyons premièrement la définition de notre fonction Affiche_arme(). Nous lui passons une liste d'initialisation d'éléments de type ''string''. Remarquez que le corps de la fonction est écrit pour afficher tous les éléments de la liste et que nous utilisons à nouveau les méthodes begin() et end() de la Standard Library pour parcourir cette liste.

   Maintenant on jette un coup d'oeil à notre fonction main(). Dans celle-ci, nous créons premièrement une liste d'initialisation de type string contenant (dans un ordre quelconque) des armes de traits et des armes contondantes.


   Nous initialisons ensuite deux tableaux, ''armes_contondantes[]'' et ''armes_de_traits[]'' qui vont contenir les types de ces armes.


   Nous créons aussi deux variables ''type_no'' et ''arme_no'' qui vont servir à choisir un nombre représentant le type d' armes ainsi qu'un nombre représentant l'arme que nous désirons.


   Chaque type d'arme ayant une quantité d'armes différentes dans sa liste, l'appel de la fonction Affiche_arme(), bien qu'écrite pour afficher la liste entière, ne nous donnera que la liste des armes du type d'une arme bien précise que nous aurons choisie. wink

   Deux screenshots différents de résultats dans la console compléteront notre propos :

 

 

   Exercice 2 :

   Il n'y aura pas de véritable challenge à vous demander dans ce chapitre crying, mais nous allons quand-même passer à un second exercice, je ne voudrais pas que vous pensiez que je vous délaisse quelque peu ! laugh

   Ecrivez une fonction qui affiche une liste d'initialisation de 10 entiers (0...9). La liste sera affichée dans la fonction et la somme de ses éléments sera renvoyée dans la fonction main() où elle sera affichée.

   Et c'est tout pour ce qui est de la théorie de ce chapitre, nous passons maintenant aux corrigés des exercices du chapitre précédent.

 

       3. Corrigés des exercices du chapitre 9 

     Exercice 1

   Il était demandé :

   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 le l'afficher à l'écran. 


   N'oubliez pas la dé-allocation du tableau avant de sortir de la fonction main()

   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. 

   Voici le code : 

//ExerciceChap9_1
//Création et destruction de variables dans le tas
#include <iostream>
#include <conio.h>
#include <string>
#include <cstdlib>
 
using namespace std;
 
void Out_Of_Memory();
int Remplir_tableau(int *val);
 
int main()
{
//Création et destruction de variables dans le tas
cout << endl;
string s = "Reservation d'un pointeur sur un tableau de 20 entiers dans le tas";
cout << s << endl << endl;
 
 
//On déclare une référence sur un entier dans le tas et on lui donne la valeur 20
int &nombre = *new int(20);
//On déclare un pointeur sur un tableau dans le tas
//La grandeur du tableau est la valeur de la référence &nombre
int *tab = new int[nombre];
 
if (!tab)
{
Out_Of_Memory();
return -1;
}
 
Remplir_tableau(tab);
 
//Affichage du tableau retourné dans la fonction main
cout << endl;
cout << "et affichage du tableau dans la fonction main()" << endl;
 
for (int i = 0; i < 20; ++i)
{
cout << tab[i];
cout << " ";
}
 
// On n'oublie pas de libérer la mémoire réservée avec new !!
delete[20]tab;
delete (int*)(&nombre);
_getch();
return 0;
}
 
void Out_Of_Memory()
{
cout << "Memoire insuffisante" << endl;
cout << "Pressez une touche pour fermer le programme" << endl;
_getch();
}
 
int Remplir_tableau(int *valeur)
{
cout << "Remplissage du tableau dans la fonction 'Remplir_tableau(int*)'" << endl;
for (int i = 0; i < 20; ++i)
valeur[i] = i;
 
return 0;
}

 

   Je ne pense pas qu'il y ait quelque chose à expliquer dans ce corrigé, les remarques du programme parlent d'elles-mêmes. wink Retenez simplement les emplacements différents de l'opérateur d'indirection ''*'' dans les cas de réservation d'un pointeur ou d'une référence dans le ''tas'' ainsi que la forme d'écriture de libération de la mémoire d'une référence. cool


     Exercice 2

   Pour cet exercice, on demandait d'ajouter une fonction ModifyVector() au projet 062VectorFunc_1 et d'ajouter les valeurs 0,1,2,3 dans le vector existant pour le réafficher dans la fonction main() en donnant sa nouvelle longueur.

   Voici le code : 

//ExerciceChap9_2
//Passage d'un vector à une fonction en lecture et en modification
#include <iostream>
#include <conio.h>
#include <vector>
 
using namespace std;
using uint = unsigned int;
 
void LireVector(const vector<uint> &);
uint ModifyVector(vector<uint> &);
 
 
int main()
{
 
// Création d'un vector 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 << endl;
ModifyVector(unVector);
cout << endl;
LireVector(unVector);
 
cout << endl;
 
_getch();
return 0;
}
 
 
void LireVector(const vector<uint> &monVector)
{
cout << "Lecture du vector dans la fonction LireVector()" << endl;
for (auto i : monVector)
{
cout << i;
cout << " ";
}
cout << endl;
cout << "Longueur du vector = " << monVector.size();
}
 
 
uint ModifyVector(vector<uint> &monVector)
{
uint i(0);
 
cout << "Modification du vector dans la fonction ModifyVector()" << endl;
while (i < 4)
{
monVector.push_back(i);
i++;
}
 
return 0;
}

 

   On passe un vector d'unsigned int à la fonction ModifyVector(). Dans l'implémentation de cette fonction, on ajoute simplement les valeurs 0, 1, 2, 3 au vector en utilisant la méthode push.back() et on procède de nouveau à la lecture du vector modifié dans la fonction main().

   Il n'y avait rien de difficile dans cet exercice. wink


     Correction de notre petit challenge (exercice 3) :

   Dans cet exercice, il fallait modifier le projet 062VectorFunc_1 du chapitre 9.

   Dans la fonction main() :

- Déclarer une constante unsigned int et lui donner la valeur 10.
- Déclarer et initialiser un tableau d'entiers non-signés avec les valeurs ( 0 – 9). Lui donner le nom de la constante précédente comme grandeur.
- A l'aide de ce tableau, initialiser un vector (v1).
- Copier le vector v1 dans un vector vide (v2).
- Appeler une fonction ModifyVector() et lui passer une référence sur v2 en paramètre.
- Appeler la fonction LireVector(vector<uint> &v2).
 
   Dans la fonction ModifyVector() :

- Compléter le vector v2 avec des unsigned int (10 – 19).

   Dans la fonction LireVector()

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


Voici le code : 

 
//ExerciceChap9_3
//Utilisation d'un tableau pour initialiser un vector
#include <iostream>
#include <conio.h>
#include <string>
#include <vector>
 
using namespace std;
using uint = unsigned int;
 
 
int ModifyVector(vector<uint> &);
void LireVector(const vector <uint> &);
 
 
int main()
{
//Création et destruction de variables dans le tas
cout << endl;
string s = "Utilisation d'un tableau pour initialiser un vector";
cout << s << endl << endl;
 
const uint nombre(10);
 
//On remplit le tableau avec 10 uint
uint tab[nombre] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 
//On initialise un vector v1 avec le tableau tab
vector<uint> v1(begin(tab), end(tab));
 
// On copie v1 dans un vecteur vide v2
vector<uint> v2(v1);
 
cout << "Dans main(), v1 = ";
LireVector(v1);
 
ModifyVector(v2);
cout << "Apres modification de v2" << endl;
cout << "v2 = ";
LireVector(v2);
 
_getch();
return 0;
}
 
 
int ModifyVector(vector<uint> &monVector)
{
for (uint i = 10; i != 20; ++i)
monVector.push_back(i);
cout << endl;
 
return 0;
}
 
 
void LireVector(const vector<uint> &monVector)
{
for (auto it : monVector)
cout << it << " ";
 
cout << endl;
cout << "La longueur du vector est de " << monVector.size() << " elements";
cout << endl;
} 

 

   Et bien, si je ne m'abuse, nous avons donc fait tout ce qui était demandé dans la question de l'exercice ! angel

   Remarquez la forme utilisée pour initialiser un vector avec un tableau. Retenez bien que l'expression v1(begin(tab), end(tab)) remplit le vector v1 avec tous les éléments compris dans le tableau tab[nombre], je trouve cela pas mal du tout ! Et ça vous montre également immédiatement que la longueur d'un tableau t[n] est de end(t) – begin(t)wink

   Ah j'allais oublier la petite question complémentaire surprise : Auriez-vous pu modifier v1 ? Justifiez votre réponse. wink

   Et bien, nous créons un tableau auquel ''nombre'' est déclaré ''const uint''. Le vector v1 est rempli à l'aide des valeurs contenues de ce tableau mais le mot clé const ne s'applique qu'au tableau, pas au vector v1. Le vector v1 peut donc être modifié, raccourci ou agrandi à volonté. wink

   Et voilà ce chapitre terminé. Je voudrais cependant faire une remarque sur certains exemples ou exercices proposés jusqu'à présent.

   Vous aurez remarqué que tantôt j'utilisais des chaînes avec le type ''char'', tantôt des chaînes avec le type ''string''. Auparavant, les chaînes de type ''char'' étaient surtout utilisées avec les anciens programmes C++ mais aussi actuellement pour l'interfaçage avec d'anciens programmes C. Quoi qu'il en soit, C++ recommande d'utiliser le type ''string'' avec les méthodes begin() et end() de la Standard Library. Nous verrons plus tard toute la puissance des méthodes de la library <string>, méthodes qui raccourciraient de façon très significative certaines lignes de code que nous avons jusqu'ici écrites en utilisant des chaînes de type ''char''. cool

 

   Les corrigés des exercices proposés dans ce chapitre seront présentés à la fin du chapitre 11. cool

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

            Gondulzak.  angel
 

   

 

Connexion

CoalaWeb Traffic

Today236
Yesterday274
This week1307
This month4981
Total1744188

27/04/24