Tableau dynamique – Implémentation « vanille »

Avant d'aborder diverses variantes de tableaux dynamiques avec stratégies de croissances variées, il est possible d'examiner les sources d'un tableau dynamique générique « vanille », simple :

#ifndef TABLEAU_H
#define TABLEAU_H

#include <algorithm>
class HorsBornes {};

template <class T>
   class Tableau {
   public:
      using value_type = T;
      using size_type = int;
      using reference = value_type&;
      using const_reference = const T&;
      using pointer = value_type*;
      using const_pointer = const value_type*;
      using iterator = pointer;
      using const_iterator = const_pointer;
   private:
      pointer elems;
      size_type nelems,
                cap;
   public:
      bool empty() const noexcept {
         return !size();
      }
      size_type size() const noexcept {
         return nelems;
      }
      size_type capacity() const noexcept {
         return cap;
      }
   private:
      bool full() const noexcept {
         return size() == capacity();
      }
   public:
      iterator begin() noexcept {
         return elems;
      }
      const_iterator begin() const noexcept {
         return elems;
      }
      iterator end() noexcept {
         return begin() + size();
      }
      const_iterator end() const noexcept {
         return begin() + size();
      }
      Tableau() noexcept : nelems{}, cap{}, elems{} {
      }
      Tableau(size_type n, const_reference val) : nelems{n}, cap{n}, elems{new value_type[n]} {
         try {
            std::fill(begin(), end(), val);
         } catch(...) {
            delete [] elems;
            throw;
         }
      }
      Tableau(const Tableau &autre) : nelems{ autre.size() }, cap{ autre.size() }, elems{ new value_type[autre.size()] } {
         try {
            std::copy(autre.begin(), autre.end(), begin());
         } catch(...) {
            delete [] elems;
            throw;
         }
      }
      Tableau(Tableau &&autre) : nelems{ std::move(autre.nelems) }, cap{ std::move(autre.cap) } elems{ std::move(autre.elems) } {
         autre.nelems = {};
         autre.cap = {};
         autre.elems = {};
      }
      //
      // ... etc.
      //
      ~Tableau() {
         delete [] elems;
      }
      void swap(Tableau &autre) {
         using std::swap;
         swap(elems, autre.elems);
         swap(cap, autre.cap);
         swap(nelems, autre.nelems);
      }
      Tableau& operator=(const Tableau &autre) {
         Tableau{ autre }.swap(*this);
         return *this;
      }
      Tableau& operator=(Tableau &&autre) {
         delete [] elems;
         nelems = std::move(autre.nelems);
         cap = std::move(autre.cap);
         elems = std::move(autre.elems);
         autre.nelems = {};
         autre.cap = {};
         autre.elems = {};
         return *this;
      }
      void push_back(const_reference val) {
         if (full())
            grow();
         elems[size()] = val;
         ++nelems;
      }
   private:
      void grow() {
         auto nouv_cap = capacity()? 2 * capacity() : 128; // arbitraire
         auto p = new value_type[nouv_cap];
         try {
            std::copy(begin(), end(), p);
            delete [] elems;
            elems = p;
            cap = nouv_cap;
         } catch(...) {
            delete [] p;
            throw;
         }
      }
   public:
      const_reference at(size_type n) const {
         if (size() < n) throw HorsBornes{};
         return elems[n];
      }
      const_reference operator[](size_type n) const {
         return elems[n];
      }
      reference at(size_type n) {
         if (size() < n) throw HorsBornes{};
         return elems[n];
      }
      reference operator[](size_type n) {
         return elems[n];
      }
      bool operator==(const Tableau &tab) const {
         return size() == tab.size() && std::equal(begin(), end(), tab.begin());
      }
      bool operator!=(const Tableau &tab) const {
         return !(*this == tab);
      }
   };

#endif

Notez que cette implémentation serait significativement allégée en plaçant la responsabilité de la gestion de la mémoire dans un std::unique_ptr :

#ifndef TABLEAU_H
#define TABLEAU_H

#include <algorithm>
#include <memory>
class HorsBornes {};

template <class T>
   class Tableau {
   public:
      using value_type = T;
      using size_type = int;
      using reference = value_type&;
      using const_reference = const T&;
      using pointer = value_type*;
      using const_pointer = const value_type*;
      using iterator = pointer;
      using const_iterator = const_pointer;
   private:
      std::unique_ptr<value_type[]> pointer elems;
      size_type nelems,
                cap;
   public:
      bool empty() const noexcept {
         return !size();
      }
      size_type size() const noexcept {
         return nelems;
      }
      size_type capacity() const noexcept {
         return cap;
      }
   private:
      bool full() const noexcept {
         return size() == capacity();
      }
   public:
      iterator begin() noexcept {
         return elems.get();
      }
      const_iterator begin() const noexcept {
         return elems.get();
      }
      iterator end() noexcept {
         return begin() + size();
      }
      const_iterator end() const noexcept {
         return begin() + size();
      }
      Tableau() noexcept : nelems{}, cap{}, elems{} {
      }
      Tableau(size_type n, const_reference val) : nelems{n}, cap{n}, elems{new value_type[n]} {
         std::fill(begin(), end(), val);
      }
      Tableau(const Tableau &autre) : nelems{ autre.size() }, cap{ autre.size() }, elems{ new value_type[autre.size()] } {
         std::copy(autre.begin(), autre.end(), begin());
      }
      Tableau(Tableau &&) = default;
      //
      // ... etc.
      //
      ~Tableau() = default;
      void swap(Tableau &autre) {
         using std::swap;
         swap(elems, autre.elems);
         swap(cap, autre.cap);
         swap(nelems, autre.nelems);
      }
      Tableau& operator=(const Tableau &autre) {
         Tableau{ autre }.swap(*this);
         return *this;
      }
      Tableau& operator=(Tableau &&) = default;
      void push_back(const_reference val) {
         if (full())
            grow();
         elems[size()] = val;
         ++nelems;
      }
   private:
      void grow() {
         using namespace std;
         auto nouv_cap = capacity()? 2 * capacity() : 128; // arbitraire
         unique_ptr<value_type[]> p { new value_type[nouv_cap] };
         copy(begin(), end(), p);
         swap(elems, p);
         cap = nouv_cap;
      }
   public:
      const_reference at(size_type n) const {
         if (size() < n) throw HorsBornes{};
         return elems[n];
      }
      const_reference operator[](size_type n) const {
         return elems[n];
      }
      reference at(size_type n) {
         if (size() < n) throw HorsBornes{};
         return elems[n];
      }
      reference operator[](size_type n) {
         return elems[n];
      }
      bool operator==(const Tableau &tab) const {
         return size() == tab.size() && std::equal(begin(), end(), tab.begin());
      }
      bool operator!=(const Tableau &tab) const {
         return !(*this == tab);
      }
   };

#endif

En espérant que cela vous soit utile!


Valid XHTML 1.0 Transitional

CSS Valide !