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 (https://wandbox.org/permlink/JfKjdXpDqFfoUvlE) :

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

class HorsBornes {};
 
template <class T>
   class Tableau {
   public:
      using value_type = T;
      using size_type = int;
      using reference = value_type&;
      using const_reference = const value_type&;
      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:
      [[nodiscard]] bool empty() const noexcept {
         return !size();
      }
      [[nodiscard]] size_type size() const noexcept {
         return nelems;
      }
      [[nodiscard]] size_type capacity() const noexcept {
         return cap;
      }
   private:
      [[nodiscard]] bool full() const noexcept {
         return size() == capacity();
      }
   public:
      [[nodiscard]] iterator begin() noexcept {
         return elems;
      }
      [[nodiscard]] const_iterator begin() const noexcept {
         return elems;
      }
      [[nodiscard]] const_iterator cbegin() const noexcept {
         return begin();
      }
      [[nodiscard]] iterator end() noexcept {
         return begin() + size();
      }
      [[nodiscard]] const_iterator end() const noexcept {
         return begin() + size();
      }
      [[nodiscard]] const_iterator cend() const noexcept {
         return end();
      }
      //
      // constructeur par défaut
      //
      Tableau() = default;
      //
      // constructeur paramétrique
      //
      Tableau(size_type n, const_reference val) : nelems{ n }, elems{ new value_type[n] }, cap{ n } {
         try {
            std::fill(begin(), end(), val);
         } catch(...) {
            delete [] elems;
            throw;
         }
      }
      //
      // constructeur de copie
      //
      Tableau(const Tableau &autre) : nelems{ autre.size() }, elems{ new value_type[autre.size()] }, cap{ autre.size() } {
         try {
            std::copy(autre.begin(), autre.end(), begin());
         } catch(...) {
            delete [] elems;
            throw;
         }
      }
      //
      // constructeur par liste d'initialisation
      //
      Tableau(std::initializer_list<value_type> src) : nelems{ src.size() }, cap{ src.size() }, elems{ new value_type[src.size()] } {
         try {
            std::copy(src.begin(), src.end(), begin());
         } catch(...) {
            delete [] elems;
            throw;
         }
      }
      //
      // constructeur de séquence (pourrait bénificier de enable_if ou
      // des concepts de C++ 20)
      //
      template <class It>
         Tableau(It debut, It fin) : nelems{ std::distance(debut, fin) } {
            cap = size();
            elems = new value_type[capacity()];
            try {
               std::copy(debut, fin, begin());
            } catch(...) {
               delete [] elems;
               throw;
            }
         }
      //
      // constructeur de conversion
      //
      template <class U>
         Tableau(const Tableau<U> &autre) : nelems{ autre.size() }, elems{ new value_type[autre.size()] }, cap{ autre.size() } {
            try {
               std::copy(autre.begin(), autre.end(), begin());
            } catch(...) {
               delete [] elems;
               throw;
            }
         }
      //
      // constructeur de mouvement
      //
      Tableau(Tableau &&autre)
         : nelems{ std::exchange(autre.nelems, 0) }, elems{ std::exchange(autre.elems, nullptr) }, cap{ std::exchange(autre.cap, 0) } {
      }
      ~Tableau() {
         delete [] elems;
      }
      void swap(Tableau &autre) {
         using std::swap;
         swap(elems, autre.elems);
         swap(nelems, autre.nelems);
         swap(cap, autre.cap);
      }
      //
      // affectation
      //
      Tableau& operator=(const Tableau &autre) {
         Tableau{ autre }.swap(*this);
         return *this;
      }
      //
      // affectation covariante
      //
      template <class U>
         Tableau& operator=(const Tableau<U> &autre) {
            Tableau{ autre }.swap(*this);
            return *this;
         }
      //
      // affectation par mouvement
      //
      Tableau& operator=(Tableau &&autre) {
         Tableau{ std::move(autre) }.swap(*this);
         return *this;
      }
   private:
      void grow() {
         const auto nouv_cap = capacity()? capacity() * 2 : 128;
         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:
      void push_back(const_reference val) {
         if (full())
            grow();
         elems[size()] = val;
         ++nelems;
      }
      [[nodiscard]] const_reference at(size_type n) const {
         if (size() < n) throw HorsBornes{};
         return elems[n];
      }
      [[nodiscard]] reference at(size_type n) {
         if (size() < n) throw HorsBornes{};
         return elems[n];
      }
      [[nodiscard]] const_reference operator[](size_type n) const {
         return elems[n];
      }
      [[nodiscard]] reference operator[](size_type n) noexcept {
         return elems[n];
      }
      [[nodiscard]] bool operator==(const Tableau &tab) const {
         return size() == tab.size() &&
                std::equal(begin(), end(), tab.begin());
      }
      [[nodiscard]] 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 (https://wandbox.org/permlink/hjBRprrGk5jlaKPg) :

#ifndef TABLEAU_H
#define TABLEAU_H
 
#include <algorithm>
#include <iterator>
#include <utility>
#include <initializer_list>
#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 value_type&;
      using pointer = value_type*;
      using const_pointer = const value_type*;
      using iterator = pointer;
      using const_iterator = const_pointer;
   private:
      std::unique_ptr<value_type[]> elems {};
      size_type nelems {},
                cap {};
   public:
      [[nodiscard]] bool empty() const noexcept {
         return !size();
      }
      [[nodiscard]] size_type size() const noexcept {
         return nelems;
      }
      [[nodiscard]] size_type capacity() const noexcept {
         return cap;
      }
   private:
      [[nodiscard]] bool full() const noexcept {
         return size() == capacity();
      }
   public:
      [[nodiscard]] iterator begin() noexcept {
         return elems.get();
      }
      [[nodiscard]] const_iterator begin() const noexcept {
         return elems.get();
      }
      [[nodiscard]] const_iterator cbegin() const noexcept {
         return begin();
      }
      [[nodiscard]] iterator end() noexcept {
         return begin() + size();
      }
      [[nodiscard]] const_iterator end() const noexcept {
         return begin() + size();
      }
      [[nodiscard]] const_iterator cend() const noexcept {
         return end();
      }
      //
      // constructeur par défaut
      //
      Tableau() = default;
      //
      // constructeur paramétrique
      //
      Tableau(size_type n, const value_type &val) : nelems{ n }, elems{ new value_type[n] }, cap{ n } {
         std::fill(begin(), end(), val);
      }
      //
      // constructeur de copie
      //
      Tableau(const Tableau &autre) : nelems{ autre.size() }, elems{ new value_type[autre.size()] }, cap{ autre.size() } {
         std::copy(autre.begin(), autre.end(), begin());
      }
      //
      // constructeur par liste d'initialisation
      //
      Tableau(std::initializer_list<value_type> src) : nelems{ src.size() }, cap{ src.size() }, elems{ new value_type[src.size()] } {
         std::copy(src.begin(), src.end(), begin());
      }
      //
      // constructeur de séquence (pourrait bénificier de enable_if ou
      // des concepts de C++ 20)
      //
      template <class It>
         Tableau(It debut, It fin) : nelems{ std::distance(debut, fin) } {
            cap = size();
            elems.reset(new value_type[capacity()]);
            std::copy(debut, fin, begin());
         }
      //
      // constructeur de conversion
      //
      template <class U>
         Tableau(const Tableau<U> &autre) : nelems{ autre.size() }, elems{ new value_type[autre.size()] }, cap{ autre.size() } {
            std::copy(autre.begin(), autre.end(), begin());
         }
      //
      // constructeur de mouvement
      //
      Tableau(Tableau &&) = default;
      ~Tableau() = default;
      void swap(Tableau &autre) {
         using std::swap;
         swap(elems, autre.elems);
         swap(nelems, autre.nelems);
         swap(cap, autre.cap);
      }
      //
      // affectation
      //
      Tableau& operator=(const Tableau &autre) {
         Tableau{ autre }.swap(*this);
         return *this;
      }
      //
      // affectation covariante
      //
      template <class U>
         Tableau& operator=(const Tableau<U> &autre) {
            Tableau{ autre }.swap(*this);
            return *this;
         }
      //
      // affectation par mouvement
      //
      Tableau& operator=(Tableau &&) = default;
   private:
      void grow() {
         const auto nouv_cap = capacity()? capacity() * 2 : 128;
         auto p = std::make_unique<value_type[]>(nouv_cap);
         std::copy(begin(), end(), p.get());
         elems.swap(p);
         cap = nouv_cap;
      }
   public:
      void push_back(const_reference val) {
         if (full())
            grow();
         elems[size()] = val;
         ++nelems;
      }
      [[nodiscard]] const_reference at(size_type n) const {
         if (size() < n) throw HorsBornes{};
         return elems[n];
      }
      [[nodiscard]] reference at(size_type n) {
         if (size() < n) throw HorsBornes{};
         return elems[n];
      }
      [[nodiscard]] const_reference operator[](size_type n) const {
         return elems[n];
      }
      [[nodiscard]] reference operator[](size_type n) noexcept {
         return elems[n];
      }
      [[nodiscard]] bool operator==(const Tableau &tab) const {
         return size() == tab.size() &&
                std::equal(begin(), end(), tab.begin());
      }
      [[nodiscard]] 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 !