| | 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. |