The C ++ 11 standard has the following lines in general container requirements.
(23.2.1 - 3)
For components affected by this subclause declaring allocator_type, the objects stored in these components must be constructed using the allocator_traits :: construct function and destroyed using the allocator_traits :: destroy function (20.6.8.2). These functions are called only for the container element type, not for the internal types used by the container.
(23.2.1 - 7)
Unless otherwise specified, all containers defined in this section receive memory using a allocator
Is it true or not that all the memory used by the container is allocated by the indicated allocator? Since the standard says that inner types are not created with allocator_traits :: construct, therefore there must be some call for the new operator. But the standard also says that all containers defined in this section receive memory using a allocator, which, in my opinion, means that it cannot be an ordinary new operator, it must be hosted by a new operator. Am I right?
Let me show you an example of why this is important.
Let's say we have a class that contains some allocated memory:
#include <unordered_map> #include <iostream> #include <cstdint> #include <limits> #include <memory> #include <new> class Arena { public: Arena(std::size_t size) { size_ = size; location_ = 0; data_ = nullptr; if(size_ > 0) data_ = new(std::nothrow) uint8_t[size_]; } Arena(const Arena& other) = delete; ~Arena() { if(data_ != nullptr) delete[] data_; } Arena& operator =(const Arena& arena) = delete; uint8_t* allocate(std::size_t size) { if(data_ == nullptr) throw std::bad_alloc(); if((location_ + size) >= size_) throw std::bad_alloc(); uint8_t* result = &data_[location_]; location_ += size; return result; } void clear() { location_ = 0; } std::size_t getNumBytesUsed() const { return location_; } private: uint8_t* data_; std::size_t location_, size_; };
we also have a custom dispenser:
template <class T> class FastAllocator { public: typedef T value_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; template <class U> class rebind { public: typedef FastAllocator<U> other; }; Arena* arena; FastAllocator(Arena& arena_): arena(&arena_) {} FastAllocator(const FastAllocator& other): arena(other.arena) {} template <class U> FastAllocator(const FastAllocator<U>& other): arena(other.arena) {}
Here's how we use it:
typedef std::unordered_map<uint32_t, uint32_t, std::hash<uint32_t>, std::equal_to<uint32_t>, FastAllocator<std::pair<uint32_t, uint32_t>>> FastUnorderedMap; int main() {
As you can see, the unordered_map destructor is never called, but memory is freed during the destruction of the arena object. Will there be a memory leak and why?
I would really appreciate any help on this topic.