énoncé
Transcription
énoncé
Master de sciences et technologie 1 Mention : mathématiques et applications 4M016 Initiation au C++ 2016-2017 TP 5 : Classes et allocation dynamique de mémoire 1 Tableaux de taille modiable Le but de cet exercice est d'écrire une classe qui sera nommée Table et qui doit représenter un tableau de double dont la taille puisse évoluer au cours de l'execution. Ecrire une première version de la classe Table contenant une donnée membre nommée data de type double* (un tableau alloué dynamiquement donc), ainsi qu'une donnée membre nommée size de type int et représentant la taille éective du tableau. La classe Table sera par ailleurs munie des fonctions membre suivantes : 1) • Un constructeur par défaut générant un tableau vide de taille eective 0. • Un constructeur prenant en argument un entier N générant un tableau de taille eective N , avec les entrée initialisées à 0. • Un destructeur qui gère la désallocation de la mémoire. • Un operateur = d'aectation par recopie dont on supposera pour le moment qu'il ne peut eectuer que des recopies entre des instances de Table dont la valeur de size coincide. • Une méthode nommée GetSize qui ne prend pas d'argument d'entrée et renvoie un int en sortie dont la valeur coincide avec la valeur de size. • Un opérateur d'accès [ ] prenant un int en argument d'entrée, et renvoyant un double en sortie tel que si T est un objet de type Table, alors T[j] renvoie le j+1 ième élément (on suppose que la numérotation commence à zéro comme pour les tableaux standard du C++). On écrira une version simple (lecture et écriture autorisée) et une version const (lecture autorisée uniquement) de cet opérateur. De plus l'appel de T[j] pour un j ≥T.GetSize() devra générer une erreur (évitant ainsi les dépassements de tableau). • Un opérateur de ux de sortie << permettant par exemple un achage dans le shell à l'aide de std::cout. On veillera tout particulièrement à assurer une gestion propre de l'allocation dynamique de mémoire par les constructeurs et l'opérateur d'aectation par recopie. 1 Ajoutez une fonction membre dont la déclaration est void Resize(int& ) qui a pour but de modier la taille d'un Table de la manière suivante. Si T est un objet de type Table, alors l'instruction T.Resize(j) xe la valeur de size à j , désalloue le pointeur data, et réalloue à nouveau à data un tableau dynamique de taille j . 2) Vous modierez ensuite l'opérateur = d'aection par recopie à l'aide de Resize de façon à pouvoir eectuer des aectation entre des Table de taille diérente. Dans cette question vous écrirez une autre version de Resize, mais en adoptant une stratégie de réallocation de mémoire diérente. Notons N la taille de data, c'est-à-dire la mémoire qui lui a été allouée en tant que tableau dynamique. On ne supposera plus ici que N = size, mais plutôt que N = exp(bln(size)c + 1), ce qui permet en particulier de garantir que N ≥ size. 3) A l'appel de T.Resize(j), à nouveau on aectera dans un premier temps la valeur j à size. Si la condition N ≥ exp(bln(j)c + 1) est violée, on désallouera le pointeur data pour lui réallouer la valeur exp(bln(j)c + 1). Enn, si cette réallocation a lieu, on eectuera des recopies pour assurer que l'ancien contenu de T se retrouve toujours dans le tableau réalloué. Vous veillerez à ce que l'opérateur = d'aection par recopie, ainsi que les constructeurs soient cohérents avec ce nouveau principe de gestion del la mémoire. 4) A l'aide de courbes tracées à l'aide gnuplot, vous eectuerez des tests sur l'appel de Resize 2 Liste chainée Le but de cet exercice est la construction d'une classe modélisant une liste à l'aide d'une structure de liste simplement chaînée. Dans ce type de structure, chaque élément de la liste (modélisée par la classe elt) contient deux membres : • le membre data : est la donnée contenu dans cette "case" de la liste • le membre next : est un pointeur sur l'élément suivant de la liste Si l'élément considéré est le dernier élément de la liste, son membre next doit être nul. La classe list elle-même ne comporte que deux membres : • le membre nb_elt : est le nombre d'éléments dans la liste • le membre first : est un pointeur sur le premier élément de la liste 2 #i n c l u d e <iostream > #i n c l u d e <c a s s e r t > u s i n g namespace std ; //=========================// // C l a s s e élément // //=========================// class elt { public : e l t ∗ next ; double data ; }; e l t ( e l t ∗ n , c o n s t double& d = 0 ) : next ( n ) , data ( d ) { } ; ~ e l t ( ) { i f ( next ){ d e l e t e next ; } } void swap ( ) ; //=========================// // Classe l i s t e // //=========================// class list { elt∗ first ; i n t nb_elt ; public : l i s t ( ) : f i r s t ( 0 ) , nb_elt ( 0 ) { } ; ~ l i s t ( ) { i f ( f i r s t ){ d e l e t e f i r s t ; } } i n t s i z e ( ) { r e t u r n nb_elt ; } double& o p e r a t o r [ ] ( c o n s t i n t &); void push ( c o n s t double &); double pop ( ) ; f r i e n d ostream& operator <<(ostream &, l i s t &); void s o r t ( ) ; }; f r i e n d l i s t & merge ( c o n s t l i s t &, c o n s t l i s t &); f r i e n d void s o r t ( l i s t &); 3 1) Écrire l'opérateur admettant l'entête double& operator[](const int& k) permettant d'accéder à la donnée (i.e. le membre data) du kème élément de la liste. 2) Écrire la méthode admettant pour entête void push(const double& e) qui instancie un nouvel élément et le place en tête de la liste. Cette instanciation devra être dynamique (objet créé avec new) pour que l'instance ne soit pas détruite à la sortie de la méthode push. 3) Écrire la méthode admettant pour entête double pop() qui détruit l'élément en tête de la liste et renvoie une copie de la donnée qu'il contenait. 4) Écrire l'opérateur de ux de sortie ostream& operator<<(ostream& os, list& l) qui permet un achage de l'ensemble des éléments de la liste en la parcourant du début à la n. 5) Écrire la méthode void swap() qui échange le contenu d'un élément et de l'élément suivant de la liste. On prendra soin de vérier que l'élément courant n'est pas le dernier de la liste (de sorte que l'élément suivant existe bien !). Pour écrire cette fonction membre on s'autorise à modier la valeur du champ data de la classe elt. 6) Écrire la méthode void sort() qui doit trier par ordre croissant les données contenues dans les diérents éléments de la liste en utilisant la méthode swap de la classe elt. Pour simplier on considèrera l'algorithme naif de tri à bulle (cf. TP2). 7) Ecrivez la fonction amie merge qui prend en argument deux list dont le contenu est supposé trié par ordre croissant, et renvoie en sortie une nouvelle list triée obtenue par fusion des deux arguments d'entrée suivant le principe de la routine merge présenté dans le TP2 pour l'algorithme de tri fusion. 8) Ecrivez la fonction amie sort qui implémente l'algorithme de tri fusion appliquée à la structure de liste chaînée considérée dans cet exercice. 4