Série 11
Pointeurs et références
But
Cette série a pour but de vous faire pratiquer les notions de pointeurs et de références. Ces notions ne seront abordées ici que de façon très simple. Le second semestre vous donnera l'occasion de beaucoup travailler avec les pointeurs.
Création du projet sous QTCreator
Afin de vous inciter à vous familiariser avec QTCreator, une façon simplifiée de configurer cet outil vous est proposé dans cette série. Inspirez-vous de la vidéo de la semaine 4 en utilisant cette fois l'archive serie11.zip.
Exercice 0 : listes chaînées (pointeurs, niveau 0)
Le but de cet exercice est de travailler les pointeurs (et les structures) sur une notion très connue en informatique pour organiser les données : les listes chaînées.
Cliquez ici si vous souhaitez faire cet exercice.
Exercice 1 : Pointeurs et références (niveau 1)
Question 1 :
Le programme suivant fait des manipulations, en apparence presque identiques, au moyen d'une référence puis au moyen d'un pointeur :
#include<iostream>;
using namespace std;
int main()
{
int i(1);
int& ri(i);
int* ptri(&i);
int j;
// avec la référence
j = 2;
ri = j;
j = 3;
cout << i << endl;
cout << j << endl;
cout << ri << endl;
// avec le pointeur
j = 2;
ptri = &j;
j = 3;
cout << i << endl;
cout << j << endl;
cout << *ptri << endl;
return 0;
}
Indiquez, sans exécuter le code, l'affichage produit par ce programme (aidez-vous de diagramme "boîtes-et-flêche") puis vérifiez votre réponse.
Expliquez le fonctionnement de ce programme.
Question 2 :
Parmi les 6 déclarations suivantes, indiquez lesquelles poseront problème à la compilation sachant que la déclaration :
int i(1)
| 1 | int& ri(i); |
| 2 | int* ptri(&i); |
| 3 | int& rj(0); |
| 4 | int* ptrj(0); |
| 5 | int& rk; |
| 6 | int* ptrk; |
Exercice 2 : Généricité (pointeurs, niveau 1)
Un petit exercice simple pour manipuler les pointeurs comme sélecteurs d'objets.
Créez un nouveau fichier appelé selecteur.cc.
Dans le main(), créez trois variables valeur1 valeur2 et valeur3, de type double que
vous
initialisez à des valeurs de votre choix.
Déclarez un pointeur choix, pointeur sur des double.
Demandez ensuite un nombre entre 1 et 3 à l'utilisateur (vous pouvez par exemple utiliser la fonction demander_nombre telle que définie ici ) et, en fonction de son choix, faites pointer choix sur valeur1, valeur2 ou valeur3.
Pour finir affichez Vous avez choisi et la valeur choisie en utilisant le pointeur choix.
Remarque : il est évident que le but de cet
exercice
n'est que de manipuler les pointeurs. Hors de ces contraintes, la
bonne solution pour faire la même chose serait bien sûr
d'utiliser un
tableau.
Une meilleure illustration, mais plus complexe, de l'utilisation
des pointeurs dans le choix d'objets est donnée dans
l'exercice 4.
Question subsidiaire : Pourrait-on faire la même chose avec une référence ?
Exercice 3 : référence (structures, références, niveau 1)
Un petit exercice simple pour manipuler les références (C++) comme référencement d'objets (cas d'utilisation numéro 1 du cours).
3.1 Références
Créez une structure Maison contenant simplement une adresse (chaîne de caractères).
Créez ensuite une structure Personne contenant un nom (chaîne de caractères) et une référence sur une Maison.
Les différents habitants d'une maison n'ont en effet pas une copie de leur maison avec eux, mais simplement un moyen d'y référer (mémoire de l'adresse)...
C'est un cas typique d'illustration des références.
Créez ensuite deux maisons différentes dans le main(), puis créez des personnes différentes habitant dans des maisons différentes (plusieurs habitants par maison).
La principale difficulté réside dans le fait qu'une référence doit toujours référencer quelque chose : il faut donc affecter les maisons dès l'initialisation.
Pour finir cette partie : créez une fonction affiche qui affiche une personne en indiquant son nom et son adresse, par exemple :
Pierre habite 12 rue du Château
Affichez toutes les personnes de votre programme.
3.2 Limites des références (niveau 2)
Le cadre choisi ici illustre aussi les limites des références : avec cette implémentation, il est impossible à une personne de déménager : on ne peut en effet pas «changer de référence».
C'est parfait si c'est vraiment ce que l'on veut dans le programme (=on est sûr que personne ne déménagera), et il vaudrait alors mieux la déclarer comme référence constante.
Mais si l'on voulait pouvoir faire déménager les personnes, il faudrait faire autrement... à vous de trouver comment.
En clair : implémenter une seconde version du programme dans laquelle vous faites déménager au moins une personne.
Assurez-vous que les autres personnes à la même (ancienne) adresse n'ont pas elles aussi déménagé (ce qui serait le cas avec des références :-( ).
Exercice de l'étude de cas
Pour poursuivre l'étude de cas faites en cours cette semaine, vous pouvez maintenant compléter le fichier facebook-exercice.cc. Des commentaires vous indiquent ce qui est attendu.Exercice 4 : Intégrales revisitées (pointeurs sur fonctions, niveau 2)
À réaliser seulement si vous avez encore du temps
Pointeurs sur fonction
On veut ici approfondir l'exercice 4 de la série 6 dans le sens suggéré par la dernière remarque (et vu en cours).
Copier votre programme integrale.cc dans un fichier (integrale_ptr.cc) puis éditez-le pour le modifier.
Essayez (évidemment !) de faire cet exercice sans copier les transparents correspondants du cours...
Définissez trois fonctions de votre choix qui combinent des
fonctions
mathématique définies dans cmath.
Toutes ces fonctions doivent être compatibles avec le
prototype double f(double);.
Écrivez une fonction demander_fonction ne prenant pas d'arguments et retournant un pointeur, double (*f)(double), sur une fonction de prototype double f(double) . Cette fonction devra demander à l'utilisateur un nombre entre 1 et 5 (pensez à utiliser la fonction demander_nombre disponible ici). S'il répond 1, 2 ou 3, la fonction demander_fonction renvoie le pointeur sur celle de vos fonction qui correspond. Si l'utilisateur répond 4, elle retourne un pointeur sur la fonction sinus (sin) et si il repond 5 elle retourne un pointeur sur la fonction exponentielle (exp).
typedef double (*Fonction)(double);Ceci mettra à votre disposition un nouveau type Fonction équivalent à "pointeur sur une fonction prenant en argument un double et retournant un double". Une fois cette déclaration faite, Vous pouvez écrire Fonction partout où un double (*f)(double) est nécessaire. En C++11, vous pouvez utiliser à la place:
typedef function <double(double)> Fonction;Mais il faut en plus inclure functional.
Calculez ensuite l'intégrale de la fonction à l'aide de la fonction integre (définie dans l'exercice précédent) que vous aurez modifiée de façon à ce qu'elle prenne en argument supplémentaire un objet de type Fonction.
Tableaux de pointeurs sur fonctions
Si vous ne l'avez pas déjà fait comme cela, nous allons maintenant encore raffiner le choix en implémentant un tableau représentant tous les choix possibles.
Définissez un tableau de Fonction nommé choix.
L'idée de base est de choisir la fonction directement en indexant le tableau des choix possibles. Si la réponse de l'utilisateur est stockée par exemple dans la variable rep, la bonne fonction sera alors choix[rep]. (La fonction demander_fonction renvoit maintenant un entier).
Initialisez ce tableau pour correspondre au choix précédent (f1, f2, f3, sin, exp).
Testez votre programme.
Pour finir si vous vous souvenez bien de l'utilisation des struct et que voulez encore raffiner votre programme vous pouvez créer une structure qui contient une chaîne de caractères décrivant la fonction et un objet de type Fonction.
Lors de la demande à l'utilisateur, affichez la description de la fonction pour aider l'utilisateur dans son choix.
Exemple de déroulement
Vous pouvez choisir parmi les fonctions suivantes :
1- x carré
2- racine d'exponentielle
3- log(1+sin(x))
4- sinus
5- exponentielle
De quelle fonction voulez vous calculer l'intégrale [1-5] ? 4
Entrez un nombre réel : -3.141592653589
Entrez un nombre réel : 0
Integrale de sinus entre -3.14159265359 et 0 :
-2.00001781364