| 2 | = Mon Premier Objet en C++ = |
| 3 | |
| 4 | == La Classe Vector == |
| 5 | |
| 6 | L'objectif de la classe {{{vector}}} est de fournir des objets se comportants de |
| 7 | façon identiques aux tableaux C, mais avec l'avantage d'une gestion transparente |
| 8 | de l'allocation mémoire. Dans le cadre de ce TME, nous travaillerons sur un |
| 9 | tableau d'entier. |
| 10 | |
| 11 | === Construction === |
| 12 | |
| 13 | Un {{{Vector}}} devra pouvoir être construit à partir de rien (il sera vide) |
| 14 | ou à partir d'un tableau C ordinaire. On fournira aussi une implémentation |
| 15 | du constructeur par copie. |
| 16 | |
| 17 | {{{ |
| 18 | int ordered[] = { 0, 1, 2, 3, 5 }; |
| 19 | Vector v1 ( ordered, 5 ); |
| 20 | }}} |
| 21 | |
| 22 | En plus des construct |
| 23 | |
| 24 | === Stratégie d'allocation mémoire === |
| 25 | |
| 26 | Plutôt que d'allouer un tableau sous-jacent de la taille exacte du nombre des |
| 27 | éléments contenus, le {{{Vector}}} réserve à l'avance de l'espace libre. |
| 28 | Ceci afin de limiter le nombre d'opérations de réallocation ainsi que de |
| 29 | trop fragmenter la mémoire. |
| 30 | |
| 31 | Nous sommes donc amenés à distinguer deux quantitées: |
| 32 | * Le nombres d'élements réellement utilisés du {{{Vector}}}, ce nombre sera |
| 33 | indiqué par la méthode {{{size()}}}. |
| 34 | * Le nombres d'élements que peut contenir le {{{Vector}}} ''sans avoir à être |
| 35 | ré-alloué''. Nombre indiqué par la méthode {{{capacity()}}}. |
| 36 | |
| 37 | Le tableau sous-jacent du {{{Vector}}} aura une taille strictement croissante. |
| 38 | Il se ré-allouera dans des zones mémoires de plus en plus grandes. |
| 39 | |
| 40 | '''Attention:''' lors d'une ré-allocation, la zone mémoire précédemment occupée |
| 41 | doit être libérée, ainsi qu'a la destuction de l'objet. |
| 42 | |
| 43 | La taille du vecteur doublera (sur un multiple de 2) à chaque réallocation. Ce qui |
| 44 | revient à dire qu'un {{{Vector}}} ne pourra avoir que des tailles de: 0, 1, 2, 4, 8, |
| 45 | 16, 32, 64, ... |
| 46 | |
| 47 | Cas particulier: si l'on connaît la taille ''à priori'' du vecteur, on dispose d'une |
| 48 | méthode {{{resize(size_t)}}} pour le dimensionner directement à la bonne taille. |
| 49 | |
| 50 | L'allocation mémoire sera gérée grâce aux deux méthodes privées suivantes: |
| 51 | * {{{_resize(size_t)}}}, assure la ré-allocation pour n'importe quelle capacité, |
| 52 | ne fait rien si on demande une diminition de celle-çi. Dans ce dernier cas, |
| 53 | on affichera le message d'avertissement suivant: |
| 54 | {{{[WARNING] Vector::_resize() cowardly refusing to shrink (16 to 7)}}} |
| 55 | * {{{_growPolicy(size_t newcapacity)}}}, calcule la nouvelle capacité nécessaire pour |
| 56 | pouvoir stocker {{{newcapacity}}}. |
| 57 | |
| 58 | === Ajout, retrait et accès aux éléments === |
| 59 | |
| 60 | Dans un premier temps, l'ajout et le retrait d'élément se fera uniquement en fin |
| 61 | de tableau avec les méthodes suivantes: |
| 62 | * {{{void push_back(int)}}}, ajoute un élément. |
| 63 | * {{{int pop_back()}}}, renvoie et retire le dernier élément. Dans le cas ou le {{{Vector}}} |
| 64 | est vide, afficher le message suivant: |
| 65 | {{{[ERROR] Vector::pop_back(): vector is empty."}}} |
| 66 | |
| 67 | L'accès aux éléments se fera par l'intermédiaire de l'opérateur d'accès indexé. |
| 68 | Si l'index est hors borne, afficher le message: |
| 69 | {{{[ERROR] Vector::operator[](): Index 12 is out of bound (10)"}}} |
| 70 | |
| 71 | === Identificateur === |
| 72 | |
| 73 | On désire, de plus marquer chaque {{{Vector}}} avec un identificateur unique, |
| 74 | proposer une mécanique. La méthode {{{size_t id()} renverra l'identificateur |
| 75 | du vecteur. |
| 76 | |
| 77 | === Affichage d'un {{{Vector}}} dans un flot de sortie === |
| 78 | |
| 79 | Ajouter la mécanique nécessaire à l'affichage d'un vecteur dans un flot. On désire |
| 80 | obtenir l'affichage suivant: |
| 81 | {{{<Vector id:0 5/8 [0 1 2 3 5]>}}} |
| 82 | Ou les informations sont respectivement, l'indentificateur du vecteur, le nombre |
| 83 | d'élements actuellement contenus, la capacité puis les valeurs des éléments. |
| 84 | |
| 85 | === Echange du contenu de deux {{{Vector}}} === |
| 86 | |
| 87 | On réalisera une fonction {{{void swap(Vector&)}}} qui échangera le contenu de deux |
| 88 | vecteurs. Astuce: comment utiliser cette fonction pour libérer la mémoire occupée |
| 89 | par un vecteur? |
| 90 | |
| 91 | === Petit Programme de test === |
| 92 | |
| 93 | {{{ |
| 94 | #include <iostream> |
| 95 | using namespace std; |
| 96 | |
| 97 | #include "Vector.h" |
| 98 | |
| 99 | int main ( int argc, char* argv[] ) |
| 100 | { |
| 101 | int ordered[] = { 0, 1, 2, 3, 5 }; |
| 102 | Vector v1 ( ordered, 5 ); |
| 103 | |
| 104 | cout << "v1[0] = " << v1[0] << endl; |
| 105 | cout << "v1[5] = " << v1[5] << endl; |
| 106 | cout << "v1 " << v1 << endl; |
| 107 | |
| 108 | Vector v2; |
| 109 | v2.reserve ( 5 ); |
| 110 | for ( size_t i=0 ; i<5 ; ++i ) v2.push_back ( 4-i ); |
| 111 | cout << "v2 " << v2 << endl; |
| 112 | v2.push_back ( 5 ); |
| 113 | cout << "v2 " << v2 << endl; |
| 114 | for ( size_t i=0 ; i<8 ; ++i ) v2.pop_back (); |
| 115 | cout << "v2 " << v2 << endl; |
| 116 | |
| 117 | cout << "Number of allocated vectors:" << Vector::getMaxId() << endl; |
| 118 | |
| 119 | return 0; |
| 120 | } |
| 121 | }}} |
| 122 | |
| 123 | |
| 124 | == Iterateur == |
| 125 | |
| 126 | En première approximation, un ''iterateur'' (ou {{iterator}}) est une classe |
| 127 | conçue de façon à imiter le comportement d'un pointeur. |
| 128 | |
| 129 | La classe {{{iterator}} comportera deux membres: |
| 130 | * {{{_vector}}}, le vecteur auquel il se rapporte. Peut être {{{NULL}} s'il n'est |
| 131 | apparié à aucun {{{Vector}}} |
| 132 | * {{{_index}}}, l'élement actuellement pointé dans le vecteur. Si l'itérateur est |
| 133 | invalide, on renverra le message suivant: |
| 134 | [ERROR] Vector::iterator::operator*(): invalid iterator. |
| 135 | |
| 136 | La class {{{Vector}}} s'enrichi alors de deux méthodes supplémentaires: |
| 137 | * {{{iterator begin()}}}, renvoie un iterateur sur le premier élément du |
| 138 | vecteur. |
| 139 | * {{{iterator end()}}}, renvoie un iterateur pointant sur un élement situé |
| 140 | ''virtuellement'' après le dernier élément du vecteur. |
| 141 | |
| 142 | Considérant le code suivant: |
| 143 | {{{ |
| 144 | Vector::iterator it = v1.begin (); |
| 145 | Vector::iterator end = v1.end (); |
| 146 | |
| 147 | cout << "v1:"; |
| 148 | for ( ; it != end ; ++it ) { |
| 149 | cout << " " << *it; |
| 150 | } |
| 151 | cout << endl; |
| 152 | |
| 153 | it = v1.begin(); |
| 154 | it += 3; |
| 155 | cout << "iterator begin()+3 on v1: " << *it << endl; |
| 156 | while ( not v1.empty() ) v1.pop_back (); |
| 157 | cout << "iterator begin()+3 on v1: " << *it << endl; |
| 158 | }}} |
| 159 | Ecrire le code des itérateurs de vecteurs. |
| 160 | |
| 161 | === Classes imbriqués === |
| 162 | |
| 163 | Il est possible d'imbriquer une classe dans une autre, mise en pratique dans le |
| 164 | cas d'un itérateur: |
| 165 | {{{ |
| 166 | class Vector { |
| 167 | public: |
| 168 | class Iterator { |
| 169 | public: |
| 170 | Iterator ( Vector* v=NULL, size_t index=0 ); |
| 171 | }; |
| 172 | // ... |
| 173 | }; |
| 174 | |
| 175 | Vector::Iterator::Iterator ( Vector* v, size_t index ) |
| 176 | { } |
| 177 | }}} |
| 178 | |
| 179 | Il faut simplement répéter le préfix {{{Vector::}}} devant les définitions des |
| 180 | fonctions membres à l'extérieur de la classe. |