容器使用的内部类型的内存分配
Memory allocation of internal types used by the containers
C++11标准在通用容器要求中有以下几行。
(23.2.1-3)
对于受本子条款影响的声明分配器类型的组件,存储在这些组件中的对象应使用分配器_ raits::construct函数构造,并使用分配器_ 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;
}
正如您所看到的,undered_map的析构函数从未被调用,但内存在竞技场对象的销毁过程中被释放。会有内存泄漏吗?为什么?
我真的很感激在这个话题上能得到任何帮助。
分配器应该提供4个功能(此处感兴趣):
- 2用于内存管理:
allocate
/deallocate
- 2用于对象生存期管理:
construct
/destroy
您引用的这些函数仅适用于construct
和destroy
(在前一句中提到),而不适用于allocate
/deallocate
,因此不存在矛盾。
现在,关于内存泄漏,为了让arena分配器工作,容器中的对象不仅应该使用arena分配器来构建(容器保证),而且这些对象分配的所有内存也应该从这个分配器中获得;不幸的是,这可能会变得稍微复杂一些。
- 在C++中打印指向不同基元数据类型的指针的内存地址
- 类型总是使用其大小存储在内存中吗
- 使用模板类的自定义列表类型中的内存泄漏
- 具有 STL 向量类型成员的类的复制内存
- 内存中的长长类型表示形式
- 将基本类型数组中的内存重新用于不同(但仍然是基本)类型的数组合法吗
- 如果一个变量在它之前释放了另一个(相同的数据类型)变量,如何将其分配给内存?
- 在这种特殊情况下,我是否需要在共享内存中使用原子类型
- 为模板参数类型中的新对象分配内存
- 如何创建非 POD 类型的连续内存池?
- 内存效率:模板与基本数据类型
- POD 结构(相同类型的成员):成员是否位于连续的内存位置?
- 避免在禁用复制的 POD 类型的内存上使用"-Wclass-memaccess"
- 将分配的内存与基本数据类型一起使用时,是否需要新放置? std::complex?
- 数据对齐:限制内存地址为数据类型大小倍数的原因
- 如何为多态性中的指定类型分配内存
- malloc - 运行时内存指针类型分配
- 通过指向非多态类型的基类的指针获取已分配内存的地址
- c++和c#中int类型内存的区别
- 在c++中分配和使用无类型内存块的正确方法是什么?