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!