é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