2016-04-09 54 views
12

8 lat temu, Stephen Lavavej opublikował this blog post zawierający prostą implementację przydziału, nazwaną "Mallocator". Od tego czasu przeszliśmy do ery C++ 11 (i wkrótce C++ 17) ... czy nowe funkcje językowe i zasady w ogóle wpływają na Mallocator, czy też jest tak istotny, jak jest?Czy Mallocator Stephena Lavaveja jest taki sam w C++ 11?

+0

Teraz jest o wiele prostsze. –

+0

@KerrekSB: Link? Wyjaśnienie? – einpoklum

+1

Może przyjrzyj się przykładowi przydziału w połowie [tego przewodnika] (https://rawgit.com/google/cxx-std-draft/allocator-paper/allocator_example_usage.html) i zamień wszystkie wzmianki o "arenie" na ' malloc'/'free' ... –

Odpowiedz

10

Sam STL ma odpowiedź na to pytanie w swoim przemówieniu STL Features and Implementation techniques na CppCon 2014 (od 26'30).

Na slides są na github.

I połączyła treść slajdy 28 i 29 poniżej:

#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); 
    } 
}; 

pamiętać, że obsługuje poprawnie ewentualnego przepełnienia przeznaczyć.

+0

powinno być' std :: numeric_limits :: max() 'zamiast' static_cast (-1) 'mimo? – einpoklum

+0

@einpoklum Gwarantuje się, że standard jest taki sam: size_t muszą respektować mod 2^N arytmetyki gdzie N jest liczbą bitów size_t. – Arnaud

+0

To nadal złe kodowanie IMHO, aby użyć tego ostatniego, a nie pierwszego. – einpoklum

2

Zgodnie z sugestią @kerrek, oto Mallocator oparty na przydziale areny linked z usuniętą częścią areny.

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; 
    } 
}; 

Dużo mniej kodu. Niektóre cechy prorokowania.

+0

Czy możesz trochę wyjaśnić o wykryciu przepełnienia i kiedy może, ale nie musi być potrzebne? – einpoklum

+0

'allocate' trzeba rzucić na błąd; nie może zwrócić wartości null. –

+0

Deklaracja "przyjaciela" i przypisanie typu krzyżowego są obce. Byłoby dobrze, aby 'if (std :: size_t (-1)/sizeof (T) Casey