8 anni fa, Stephen Lavavej ha pubblicato this blog post contenente un'implementazione di allocatore semplice, denominata "Mallocator". Da allora siamo passati all'era del C++ 11 (e presto del C++ 17) ... le nuove caratteristiche linguistiche e le regole influenzano il Mallocator o sono ancora rilevanti così come sono?Il Mallocator di Stephen Lavavej è lo stesso in C++ 11?
risposta
STL stesso ha una risposta a questa domanda nel suo discorso STL Features and Implementation techniques a CppCon 2014 (a partire da 26'30).
Gli slides sono in github.
ho fuso il contenuto di diapositive 28 e 29 di seguito:
#include <stdlib.h> // size_t, malloc, free
#include <new> // bad_alloc, bad_array_new_length
template <class T> struct Mallocator {
typedef T value_type;
Mallocator() noexcept { } // default ctor not required
template <class U> Mallocator(const Mallocator<U>&) noexcept { }
template <class U> bool operator==(
const Mallocator<U>&) const noexcept { return true; }
template <class U> bool operator!=(
const Mallocator<U>&) const noexcept { return false; }
T * allocate(const size_t n) const {
if (n == 0) { return nullptr; }
if (n > static_cast<size_t>(-1)/sizeof(T)) {
throw std::bad_array_new_length();
}
void * const pv = malloc(n * sizeof(T));
if (!pv) { throw std::bad_alloc(); }
return static_cast<T *>(pv);
}
void deallocate(T * const p, size_t) const noexcept {
free(p);
}
};
Nota che gestisce correttamente la possibile overflow allocare.
dovrebbe essere' std :: numeric_limits
@einpoklum È garantito che lo standard sia lo stesso: size_t deve rispettare l'aritmetica mod 2^N dove N è il numero di bit di size_t. – Arnaud
E 'ancora sbagliato codificare IMHO per usare quest'ultimo piuttosto che il primo. – einpoklum
Come suggerito da @kerrek, ecco un Mallocator basato sull'allocazione dell'arena linked con la parte dell'arena eliminata.
template<class T>
struct Mallocator11 {
using value_type = T;
using pointer = T*;
using propagate_on_container_copy_assignment = std::true_type;
using propagate_on_container_move_assignment = std::true_type;
using propagate_on_container_swap = std::true_type;
Mallocator11(Mallocator11 const&) = default;
Mallocator11& operator=(Mallocator11 const&) = default;
Mallocator11()=default;
template<class U>
Mallocator11(Mallocator11<U> const&) noexcept {}
template<class U>
Mallocator11& operator=(Mallocator11<U> const&) noexcept {return *this}
pointer allocate(std::size_t n) {
if (std::size_t(-1)/sizeof(T) < n)
throw std::bad_array_new_length(); // or something else
if (!n) return nullptr; // zero means null, not throw
if(auto*r= static_cast<pointer>(malloc(n * sizeof(T))))
return r;
throw std::bad_alloc();
}
void deallocate(pointer p, std::size_t n) {
free(p);
}
template<class U>
bool operator==(Mallocator11<U> const& rhs) const {
return true;
}
template<class U>
bool operator!=(Mallocator11<U> const& rhs) const {
return false;
}
};
Molto meno codice. Alcuni tratti per la propagazione.
Puoi approfondire un po 'il rilevamento dell'overflow e quando può o non può essere necessario? – einpoklum
'allocate' ha bisogno di generare errori; non può restituire null. –
La dichiarazione 'amico' e l'assegnazione incrociata sono estranee. Sarebbe una buona idea per 'if (std :: size_t (-1)/sizeof (T)
Ora è molto più semplice. –
@KerrekSB: collegamento? Spiegazione? – einpoklum
Forse guarda l'esempio di allocatore a metà [questa guida] (https://rawgit.com/google/cxx-std-draft/allocator-paper/allocator_example_usage.html) e sostituisci tutte le menzioni di "arena" con ' malloc'/'free' ... –