2016-04-09 8 views
12

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?

+0

Ora è molto più semplice. –

+0

@KerrekSB: collegamento? Spiegazione? – einpoklum

+1

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' ... –

risposta

10

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.

+0

dovrebbe essere' std :: numeric_limits :: max() 'piuttosto che' static_cast (-1) 'però? – einpoklum

+0

@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

+0

E 'ancora sbagliato codificare IMHO per usare quest'ultimo piuttosto che il primo. – einpoklum

2

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.

+0

Puoi approfondire un po 'il rilevamento dell'overflow e quando può o non può essere necessario? – einpoklum

+0

'allocate' ha bisogno di generare errori; non può restituire null. –

+0

La dichiarazione 'amico' e l'assegnazione incrociata sono estranee. Sarebbe una buona idea per 'if (std :: size_t (-1)/sizeof (T) Casey