Voici la définition d'un nouveau type bien utile. Il s'agit d'un tableau pouvant s'étirer à volonté! La classe qui supporte ce nouveau type se nomme "vecteur".
Ici notre classe vecteur est quelque peu limitée puisqu'elle ne crée et ne gère que des tableaux dynamiques de "double". Nous verrons plus tard comment la rendre aussi souple que les tableaux standards du langage. Elle aura alors un net avantage puisqu'elle nous permettra de gérer, à notre façon, les débordements.
La classe "vecteur":
class vecteur{ private: double * tab; long taille; int indiceDeborde(long k); public: vecteur(void); vecteur(long nbVoulu); ~vecteur(void); long getTaille(void); int changerTaille(long nouvTaille); void setElem(long k, double valeur); double getElem(long k); void afficherTous(void); }; |
Le nouveau type "vecteur" nous permettra, comme ses deux constructeurs l'indiquent, de créer dès la déclaration de nos variables, des vecteurs tels que X ou tels que T. vecteur X; // appel du constructeur vide vecteur T(45); // appel du second constructeur Que vient faire ce paramètre "nbVoulu" ? Comme nous le verrons ci-dessous, il nous permettra de créer un vecteur de taille 45, c'est-à-dire un vecteur comportant 45 emplacements (de 0 à 44). La fonction "getTaille" retourne simplement la taille actuelle du vecteur. La fonction "changerTaille" permettra au programmeur-client de modifier comme bon lui semble la taille du vecteur qu'il est en train d'utiliser. Les fonctions "getElem" et "setElem" permettront au programmeur-client de consulter et modifier respectivement un emplacement du vecteur. Finalement, la fonction "afficherTous" permettra de faire afficher le vecteur dans son ensemble. Le destructeur est nécessaire ici puisque, comme nous le verrons, le tableau étant construit dynamiquement, il faut évidemment le détruire. |
vecteur::vecteur(void){ taille = 0 ; tab = 0; |
La définition du constructeur vide. Le tableau dynamique est totalement vide. Un peu comme si l'on effectuait la déclaration: double T; En utilisant la classe "vecteur", l'équivalent de cette déclaration deviendra: vecteur T; |
vecteur::vecteur(long nbVoulu){ taille = 0; tab = 0; if (nbVoulu > 0) { tab = new double[nbVoulu]; if (tab) taille = nbVoulu; } } |
Le second constructeur. L'opérateur "new" permet de créer autant d'emplacements du type désigné que nous le désirons. Sa syntaxe générale est: new type[nb] où "type" est le type de chaque emplacement et "nb" est le nombre d'emplacements de ce type désiré. Avec la syntaxe: new type On crée uniquement un emplacement du type désigné. Si le "new" réussit son travail, il retourne une valeur du type (type *) c'est à dire un pointeur sur le premier emplacement créé. Par contre, s'il ne réussit pas son travail (plus de RAM disponible), le new retourne 0. |
vecteur::~vecteur(void) { if (tab) delete[] tab; } |
La définition du destructeur. L'opérateur "delete" permet de libérer la mémoire pointée par le pointeur fourni. La syntaxe générale est: delete[] objet où "objet" est un pointeur non nul (n'égale pas zéro) pointant sur une série d'emplacements. Avec la syntaxe: delete objet On libère uniquement un seul emplacement. |
int vecteur::indiceDeborde(long k){ return (k < 0) || (k > taille-1); } |
Cette petite fonction appartenant à la section privée de la classe sert uniquement à vérifier si le nombre fourni en argument peut constituer un indice valide. |
long vecteur::getTaille(void) { return taille; } |
La petite fonction "getTaille" retourne simplement la taille actuelle du vecteur. |
void vecteur::setElem(long k, double valeur){ if (indiceDeborde(k)) { cout << "\nprobleme dans Set"; return; } tab[k] = valeur; } double vecteur::getElem(long k) { if (indiceDeborde(k)) { cout << "\nprobleme dans Get"; return 0; } return tab[k]; } |
Pour le moment, un simple message est affiché s'il y a débordement lors de la modification ou de la consultation d'un élément. Éventuellement, nous pourrions ajouter un paramètre supplémentaire pour retourner un code d'erreur. Ce serait évidemment la responsabilité du programmeur-client de tester ce code d'erreur. Plus tard, nous pourrons utiliser un mécanisme nous permettant de sortir rapidement de la fonction en lancant un cas d'erreur (il s'agira du mécanisme "try-- catch-- throw" du C++) |
void vecteur::afficherTous(void){ for (long i = 0; i < taille; i++) cout << "\ntab[" << i << "] = " << tab[i]; } |
Une simple fonction nous permettant d'afficher le contenu de tout le vecteur. |
La fonction "changerTaille" effectue le travail central de cette classe.
int vecteur::changerTaille(long nouvTaille){ |
|
if (nouvTaille < 1) { if (tab) delete [] tab; taille = 0; tab = 0; return 1 ; } |
Cette section de la fonction est discutable. On pourrait également retourner un message d'erreur dans le cas où la nouvelle taille demandée par le programmeur-client est trop petite. Ici nous avons décidé de détruire le tableau. |
double* nouveau = new double[nouvTaille]; |
On tente d'abord de créer le nouveau tableau. |
if (! nouveau) return 0; |
Si cette création n'est pas possible (manque de RAM), la fonction retourne zéro. Ce sera la responsabilité du programmeur-client de tester le résultat de cette fonction. |
if (! taille){ tab = nouveau; taille = nouvTaille; return 1; } |
Si la création a réussi et que le tableau original était vide, on initialise simplement les deux champs correctement sur le nouveau tableau et l'on retourne un succès (la valeur 1). |
long nb_objets = (taille < nouvTaille) ? taille : nouvTaille; |
On détermine quelle quantité d'emplacements peut être récupérée de l'ancien tableau. |
memcpy(nouveau,tab,nb_objets * sizeof(double)); |
On copie ce nombre d'emplacements dans le nouveau tableau. |
delete[] tab; |
On détruit l'ancien tableau. |
tab = nouveau; taille = nouvTaille; return 1; } |
On initialise les deux champs correctement sur le nouveau tableau et l'on retourne un succès. |
Le programme "vecteur.cpp" vous permettra d'effectuer quelques petits tests sur cette classe.
Récupéréz le programme et essayez-le en Turbo C++.