Tableau dynamique – Implémentation hiérarchique (B)

Tout comme l'implémentation hiérarchique (A) d'une stratégie de croissance pour un tableau dynamique, la présente implémentation met en place une infrastructure polymorphique (ici : abstraite) dans une classe parent représentant le concept de tableau et ses données, puis exprime les stratégies possibles dans des classes dérivées en spécialisant la méthode de croissance.

La version proposée ici est épurée. Pour éviter le bris d'encapsulation résultant d'attributs protégés, nous utiliserons ici une interface de croissance différente de celle proposée à l'implémentation hiérarchique (A) au sens où le rôle de l'enfant est de déterminer la nouvelle capacité du parent, pas de gérer la croissance à proprement dit. Ceci permet d'isoler les attributs complètement en les gardant privés. J'ai préféré ne pas modifier l'interface de grow() mais enrichir la classe Tableau<T> d'un contrat (méthode abstraite) impliquant de calculer la nouvelle capacité (méthode compute_newcap()).

La possibilité d'introduire autant d'enfants de Tableau que désiré accroît fortement la flexibilité en comparaison avec la stratégie implémentant grow() de manière locale. Il devient par contre moins agréable de manipuler un tableau dynamique, étant forcés de passer par une indirection (un pointeur) pour obtenir les précieux bénéfices du polymorphisme. Il est possible, en faisant fi de la généralité, de se passer ici de polymorphisme à l'externe et d'utiliser directement un enfant – l'invocation de grow() dans push_back() passera par this, un pointeur, et sera polymorphique de toute manière. Voir https://wandbox.org/permlink/uZxOirAjMk98u9zw :

#ifndef TABLEAU_H
#define TABLEAU_H
 
#include <algorithm>
#include <iterator>
#include <utility>
#include <initializer_list>

//
// - l'implémentation de grow() est implémentée dans Tableau, mais dépend
//   d'un calcul abstrait de la nouvelle capacité
//
// Modifs:
// - compute_newcap() est abstraite et protégée
// - le destructeur est polymorphique
//

class HorsBornes {};
 
template <class T>
   class Tableau {
   public:
      // using ...
   private:
      pointer elems {};
      size_type nelems {},
                cap {};
   public:
      // empty(), size(), capacity(), full()
      // begin(), cbegin(), end(), cend()
      // constructeur par défaut
      // constructeur paramétrique
      // constructeur de copie
      // constructeur par liste d'initialisation
      // constructeur de séquence
      // constructeur de conversion
      // constructeur de mouvement
      virtual ~Tableau() {
         delete [] elems;
      }
      // swap(), affectation, affectation covariante
      // affectation par mouvement
   private:
      void grow() {
         const auto nouv_cap = compute_newcap();
         using std::copy;
         auto p = new value_type[nouv_cap];
         try {
            copy(begin(), end(), p);
            delete [] elems;
            elems = p;
            cap = nouv_cap;
         } catch (...) {
            delete [] p;
            throw;
         }
      }
   protected:
      virtual size_type compute_newcap() const = 0;
   public:
      // push_back(), at(), operator[]
      // operator==, operator!=
   };

template <class T>
   struct TableauCool : Tableau<T> {
      using Tableau<T>::Tableau;
      using size_type = typename Tableau<T>::size_type;
      using Tableau<T>::capacity;
      size_type compute_newcap() const override {
         const size_type TAILLE_DEFAUT = 128;
         const float FACTEUR_CROISSANCE = 1.5f;
         return capacity()? static_cast<size_type>(capacity() * FACTEUR_CROISSANCE) : TAILLE_DEFAUT;
      }
   };

#endif

Un programme de test suit. Ce programme ajoute N éléments au tableau (notez qu'on utilise l'enfant, à la fois pour obtenir les bénéfices de son algorithme de croissance et parce que son parent est abstrait), provoquant au passsage à plus d'une reprise l'activation de la stratégie de croissance, puis affiche les éléments sur la sortie standard.

#include "Tableau.h"
#include <iostream>

//
// Modifs: on utilise TableauCool<T> plutôt que Tableau<T>
//
int main() {
   using namespace std;
   enum { N = 1'000 };
   TableauCool<int> tab;
   for (int i = 0; i < N; ++i)
      tab.push_back(i);
   for(auto n : tab) cout << n << ' ';
   cout << endl;
}

Que pensez-vous cette solution?


Valid XHTML 1.0 Transitional

CSS Valide !