POUPA Adrien, BUOB Edgar Efrei L'3 TP N°7 _Introduction_ * Quel mécanisme important des langages de programmation objet va-t-on utiliser et comment peut-on le mettre en œuvre en C++ ? On va utiliser le polymorphisme via le système d'héritage de C++. On ne se sert pas des templates dans ce TP. _Première partie_ __Classe "Expression"__ * Que peut-on dire de la méthode eval de la classe "Expression" ? Cette méthode est virtuelle pure car "= 0" à la fin de la déclaration de la méthode. * Qu’est-ce que cela implique pour la classe "Expression" ? On ne peut pas instancier la classe "Expression"; seulement utiliser des pointeurs ou des références vers elle. * Comment appelle-t-on ce genre de classe ? Une classe qui comporte au moins une méthode virtuelle pure est dite "classe abstraite". * Expliquez ce que permet de faire l’attribut _pool ? Le pool est un gestionnaire de ressources rudimentaire; il permet de - compter et enregistrer les adresses de toutes les instances "en vie" - retrouver l’adresse d’une instance par son nom - libérer toutes les instances qui ne l’ont pas été à la fin du programme * Est-ce un attribut d’instance ou un attribut de classe ? C'est un attribut de classe. * Que fait le constructeur de la classe "Expression" ? Expression(const string& nom) enregistre dans le pool l'instance actuelle. * Que fait son destructeur ? virtual ~Expression() libère l'instance actuelle du pool. * Pourquoi le destructeur de la classe "Expression" est-il virtual ? Puisque la classe ne peut être instanciée, étant donnée qu'elle est abstraite, elle est destinée à être utilisée polymorphiquement, c'est-à-dire utilisée en place d'un objet de la classe mère. On veut utiliser le destructeur de la classe instanciée. Il faut donc être sûr que le bon destructeur sera appelé, d'où le "virtual". * De quel type est la méthode "toutLiberer" ? static void toutLiberer() : fonction statique ne retournant rien, pas liée à un objet. Elle ne peut donc accéder à d'autres membres d'instances de la classe sinon les membres statiques. * Quel est son rôle ? Elle applique itérativement l'operateur delete au premier élément du pool tant que le pool n’est pas vide. * Quand sera-t-elle certainement appelée dans le programme principal ? Elle sera sûrement appelée à la fin du programme principal. * La fonction "friend ostream& operator<<(ostream&, const Expression&)" est-elle une méthode de la classe "Expression" ? La fonction est amie: sans faire partie directement de la classe "Expression", elle a accès à tous ses attributs. * Cette dernière ne pouvant pas être instanciée, à quoi sert cette fonction ? Elle sert de moule, de cadre aux autres fonctions. Ainsi, on peut appeler un objet constante, cos ou sin sans savoir précisément duquel il s'agit en le désignant via "Expression". * Quel est le nom ( _nom ) donné à une instance de la classe "Constante" ? On aura _nom = val avec Constante(double val) puisque "Constante" hérite de "Expression" C'est à dire Constante(3.0) => _nom = 3.0 __Classe "Unaire"__ * Décrire exactement ce que fait la méthode "str()" de la classe "Unaire". str() retourne une chaîne telle que "cos(exp)" ou "exp(sin)" à partir des deux arguments pris par le constructeur. * Dessiner le diagramme de classes du fichier "expression.h". Faites ressortir les classes abstraites. Expression abstraite Constante Unaire abstraite Cos Sin Une tabulation = héritage de la classe au-dessus __Synthèse__ * Quelle est la sortie standard obtenue par l’exécution du programme principal suivant ? Premiere partie enregistrer 1.0472 enregistrer cos cos(1.0472) = 0.5 liberer cos liberer 1.0472 _Deuxième partie_ * Quelle est la sortie standard obtenue par l’exécution du programme principal suivant ? Deuxieme partie (1 + (2 * sin(0.523599))) = 2 ((1 + (2 * sin(0.523599))) > 1.8) = 1 _Troisième partie_ * Pourquoi stocker la valeur d’une variable dans un attribut d’instance de la classe Variable pose problème et ne constitue pas une bonne solution ? Si on fait cela, on casse le lien entre le nom d'une variable et sa valeur dans le tableau associatif, comme c'est expliqué dans la question suivante. * Expliquez ce que permet de faire l’attribut de classe _memoire ? _memoire permet le stockage des variables sous la forme d'un tableau associatif dont la clé est le nom de la variable et la valeur sa valeur. On a _memoire[nomVariable] = valeurVariable * De quel type est la méthode effacerMemoire ? Quel est son rôle ? Quand sera-t-elle certainement appelée dans le programme principal ? static void effacerMemoire() : fonction statique ne retournant rien, pas liée à un objet. Elle ne peut donc accéder à d'autres membres d'instances de la classe sinon les membres statiques. Elle sera sûrement appelée à la fin du programme principal. * Décrire en détail ce que fait la méthode eval() de la classe Affectation . Quelle est la valeur retournée ? eval() affecte à une variable une valeur, puisqu'on affecte à _opleft la valeur (->eval) de _opright, en appelant la fonction set de la classe Variable Est retournée la valeur insérée, c'est à dire la valeur de op_right (_opright->eval()). * Quelle est la sortie standard obtenue par l’exécution du programme principal suivant ? Troisieme partie x = 3 y = 0 (y <- (1 + (2 * x))) = 7 y = 7 y = 0 * Représentez graphiquement l’état en mémoire de l’expression *a ainsi que l’état de l’attribut de classe _memoire lors de l’exécution du programme précédent, juste avant l’appel de Variable::effacerMemoire(); _memoire[x] = 0 y <- (1 + (2 * x)) y <- (1 + (2 * 3)) y <- (1 + 6) y <- 7 _memoire[y] = 7 _Quatrième partie_ Cf Conditionnel.h et Conditionnel.cpp _Cinquième partie_ * Analysez la classe Pour du fichier boucle.h en annexe. Décrivez ce que fait la fontion eval de cette classe ? Elle permet de calculer la valeur de l'expression donnée calc tant que la condition d'arrêt _cond n'est pas atteinte (soit != 0). * Dessinez l’arbre correspondant à une expression qui affecte la factorielle d’une variable x (dont la valeur a été affectée précédemment) à une variable y. On a y <- x! Par exemple, si x = 3, y <- 3! <=> y <- 1*2*3 <=> y <- 6 Soit <- / \ y * / \ 1 * / \ 2 * | 3 On peut généraliser à y <- x! soit: <- / \ y * / \ 1 * / \ 2 ... \ * | x * Ecrivez le programme principal qui construit un tel arbre en mémoire et évalue l’expression pour x=3.0 puis x=5.0. J'obtiens une boucle infinie. J'ai du me tromper dans les initialisations Pourtant, la logique ne me semble pas fausse: On stocke le résultat dans y i est la variable d'incrémentation initialisée à 3 à chaque tour, i = i-1 pour rencontrer la condition d'arrêt i = 0 à chaque tour, y reçoit x*(x+1) Dans ces conditions, il est difficile d'aller plus loin dans le TP.