C ++ 11標準には、一般コンテナ要件に次の行があります。
(23.2.1-3)
allocator_typeを宣言するこの節の影響を受けるコンポーネントの場合、これらのコンポーネントに格納されているオブジェクトは、allocator_traits ::construct関数を使用して構築され、allocator_traits :: destroy関数(20.6.8.2)を使用して破棄されます。これらの関数は、コンテナーの要素タイプに対してのみ呼び出され、コンテナーによって使用される内部タイプに対しては呼び出されません。
(23.2.1-7)
特に指定がない限り、この句で定義されているすべてのコンテナは、アロケータを使用してメモリを取得します
コンテナによって使用されるすべてのメモリが指定されたアロケータによって割り当てられるというのは本当ですか?標準では、内部型はallocator_traits :: Constructを使用せずに構築されるとされているため、演算子newを呼び出す必要があります。しかし、標準では、この句で定義されたすべてのコンテナは、アロケータを使用してメモリを取得するとも述べています。これは、私の意見では、通常の新しい演算子ではなく、配置の新しい演算子である必要があることを意味します。私は正しいですか?
例を示しましょう。なぜこれが重要なのか。
割り当てられたメモリを保持するクラスがあるとしましょう。
#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_;
};
カスタムアロケータもあります:
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) {}
//------------------------------------------------------------------------------------
pointer allocate(size_type n, std::allocator<void>::const_pointer)
{
return allocate(n);
}
pointer allocate(size_type n)
{
return reinterpret_cast<pointer>(arena->allocate(n * sizeof(T)));
}
//------------------------------------------------------------------------------------
void deallocate(pointer, size_type) {}
//------------------------------------------------------------------------------------
size_type max_size() const
{
return std::numeric_limits<size_type>::max();
}
//------------------------------------------------------------------------------------
void construct(pointer p, const_reference val)
{
::new(static_cast<void*>(p)) T(val);
}
template <class U> void destroy(U* p)
{
p->~U();
}
};
これが私たちの使い方です:
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()
{
// Allocate memory in arena
Arena arena(1024 * 1024 * 50);
FastAllocator<uint32_t> allocator(arena);
FastAllocator<std::pair<uint32_t, uint32_t>> pairAllocator(arena);
FastAllocator<FastUnorderedMap> unorderedMapAllocator(arena);
FastUnorderedMap* fastUnorderedMap = nullptr;
try
{
// allocate memory for unordered map
fastUnorderedMap = unorderedMapAllocator.allocate(1);
// construct unordered map
fastUnorderedMap =
new(reinterpret_cast<void*>(fastUnorderedMap)) FastUnorderedMap
(
0,
std::hash<uint32_t>(),
std::equal_to<uint32_t>(),
pairAllocator
);
// insert something
for(uint32_t i = 0; i < 1000000; ++i)
fastUnorderedMap->insert(std::make_pair(i, i));
}
catch(std::bad_alloc badAlloc)
{
std::cout << "--- BAD ALLOC HAPPENED DURING FAST UNORDERED MAP INSERTION ---" << std::endl;
}
// no destructor of unordered map is called!!!!
return 0;
}
ご覧のとおり、unordered_mapのデストラクタは呼び出されませんが、アリーナオブジェクトの破棄中にメモリが解放されます。メモリリークは発生しますか?その理由は何ですか?
このトピックについての助けを本当にいただければ幸いです。