Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
408 views
in Technique[技术] by (71.8m points)

c++ - Why not to inherit from std::allocator

I created my own allocator like so:

template<typename T>
class BasicAllocator
{
    public:
        typedef size_t size_type;
        typedef ptrdiff_t difference_type;
        typedef T* pointer;
        typedef const T* const_pointer;
        typedef T& reference;
        typedef const T& const_reference;
        typedef T value_type;


        BasicAllocator() throw() {};
        BasicAllocator (const BasicAllocator& other) throw() {};

        template<typename U>
        BasicAllocator (const BasicAllocator<U>& other) throw() {};

        template<typename U>
        BasicAllocator& operator = (const BasicAllocator<U>& other) {return *this;}
        BasicAllocator<T>& operator = (const BasicAllocator& other) {return *this;}
        ~BasicAllocator() {}

        pointer address (reference value) const {return &value;}
        const_pointer address (const_reference value) const {return &value;}

        pointer allocate (size_type n, const void* hint = 0) {return static_cast<pointer> (::operator new (n * sizeof (value_type) ) );}
        void deallocate (void* ptr, size_type n) {::operator delete (static_cast<T*> (ptr) );}

        template<typename U, typename... Args>
        void construct (U* ptr, Args&&  ... args) {::new (static_cast<void*> (ptr) ) U (std::forward<Args> (args)...);}
        void construct (pointer ptr, const T& val) {new (static_cast<T*> (ptr) ) T (val);}

        template<typename U>
        void destroy (U* ptr) {ptr->~U();}
        void destroy (pointer ptr) {ptr->~T();}

        size_type max_size() const {return std::numeric_limits<std::size_t>::max() / sizeof (T);} /**return std::size_t(-1);**/

        template<typename U>
        struct rebind
        {
            typedef BasicAllocator<U> other;
        };
};

But I want to know why I should never inherit from std::allocator. Is it because it doesn't have a virtual destructor? I've seen many posts saying that one should create their own and not inherit. I understand why we shouldn't inherit std::string and std::vector but what is wrong with inheriting std::allocator?

Can I inherit my class above? Or do I need a virtual destructor to do that?

Why?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

A lot of people are going to post in this thread that you should not inherit from std::allocator because it doesn't have a virtual destructor. They'll talk about polymorphism and slicing and deleting via pointer-to-base class, none of which are permitted by the allocator requirements as detailed in section 17.6.3.5 [allocator.requirements] of the standard. Until someone demonstrate that a class derived from std::allocator fails to meet one of those requirements, it's simple cargo cult mentality.

That said, there is little reason to derive from std::allocator in C++11. C++11's overhaul of allocators introduced the traits template std::allocator_traits to sit between an allocator and its users and provide reasonable defaults for many of the required features via template metaprogramming. A minimal allocator in C++11 can be as simple as:

template <typename T>
struct mallocator {
  using value_type = T;

  mallocator() = default;
  template <class U>
  mallocator(const mallocator<U>&) {}

  T* allocate(std::size_t n) {
    std::cout << "allocate(" << n << ") = ";
    if (n <= std::numeric_limits<std::size_t>::max() / sizeof(T)) {
      if (auto ptr = std::malloc(n * sizeof(T))) {
        return static_cast<T*>(ptr);
      }
    }
    throw std::bad_alloc();
  }
  void deallocate(T* ptr, std::size_t n) {
    std::free(ptr);
  }
};

template <typename T, typename U>
inline bool operator == (const mallocator<T>&, const mallocator<U>&) {
  return true;
}

template <typename T, typename U>
inline bool operator != (const mallocator<T>& a, const mallocator<U>& b) {
  return !(a == b);
}

EDIT: Proper use of std::allocator_traits isn't fully present in all standard libraries yet. For example, the sample allocator above doesn't work correctly with std::list when compiled with GCC 4.8.1 - the std::list code complains about missing members since it hasn't been updated yet.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...