Je suis un peu pressé mais je vous laisse quand même examiner les sources de l'allocateur simplet que j'ai mis au point cette semaine et à partir duquel j'ai construit mon blabla de ce matin.
L'idée ici est de consturire notre propre allocateur. Je vous rappelle qu'un allocateur est un objet responsable de gérer les règles d'allocation dynamique de mémoire pour un type T selon une stratégie qui lui est propre. On peut envisager plusieurs formes différentes pour un tel objet mais l'idée ici est de coller au format de l'allocateur standard pour offrir les mêmes services sous la même forme (mais pas selon la même implémentation), ce qui permet de suppléer notre allocateur à une classe standard comme un vecteur ou une liste.
Celui-ci utilise une instance de ArenaFixe pour gérer la mémoire allouée dynamiquement. Il est utile d'un point de vue pédagogique mais tend à donner des résultats inférieurs à ceux de l'allocateur standard pour un vecteur.
#ifndef ALLOCATEUR_ARENAFIXE_H
#define ALLOCATEUR_ARENAFIXE_H
#include "ArenaFixe.h"
#include <memory>
#include <cstdlib>
template <class T, std::size_t PopulationMax = 10'000>
class allocateur_arenafixe {
public:
using value_type = T;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
using difference_type = std::ptrdiff_t;
using size_type = std::size_t;
private:
static inline ArenaFixe<sizeof(value_type), PopulationMax> arena_;
public:
//
// Constructeurs par défaut et de conversion
// (la Sainte-Trinité est implicite)
//
allocateur_arenafixe() = default;
template<class U>
allocateur_arenafixe(const allocateur_arenafixe<U, PopulationMax>&) = default;
pointer address(reference val) const noexcept {
return &val;
}
const_pointer address(const_reference val) const noexcept {
return &val;
}
//
//
//
pointer allocate(size_type n, const_pointer hint = {}) {
return reinterpret_cast<pointer>((n != 1)? arena_.allouer_n(n) : arena_.allouer_un());
}
template<class U>
pointer allocate(size_type n, const U* hint= {}) {
return reinterpret_cast<pointer>((n != 1)? arena_.allouer_n(n) : arena_.allouer_un());
}
//
//
//
void construct(pointer p, const_reference src) {
new (static_cast<void *>(p)) value_type{src};
}
//
//
//
void deallocate(pointer p, size_type n) {
if (n == 1)
arena_.liberer_un(p);
else
arena_.liberer_n(p);
}
//
//
//
void destroy(pointer p) {
p->~value_type();
}
//
//
//
size_type max_size() const noexcept {
return PopulationMax;
}
//
// Petit brin de poésie...
//
template<class U>
struct rebind {
using other = allocateur_arenafixe<U, PopulationMax>;
};
template <class U>
allocateur_arenafixe& operator=(const allocateur_arenafixe<U>&) noexcept {
return *this;
}
};
#endif
Un programme simple se servant de cet allocateur serait le suivant.
#include "allocateur_arenafixe.h"
#include <iostream>
#include <vector>
#include <algorithm>
// ... using ...
int main() {
const size_t N = 1'000'000;
//
// Avec un aréna fixe un peu simplet comme le mien, faut être prudent
// face aux stratégies de croissance d'un conteneur (ici, N*4 me
// «pétait dans la face»!)
//
// Pour un vecteur standard de int, ma stratégie est moins bonne que la
// stratégie standard. Faudrait donc la raffiner.
//
vector<int, allocateur_arenafixe<int, N * 8>> vaf;
for (int i = 0; i < N; ++i)
vaf.push_back(i);
}