gpt4 book ai didi

c++ - 容器使用的内部类型的内存分配

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:24:43 25 4
gpt4 key购买 nike

C++11 标准在一般容器要求中有以下几行。

(23.2.1 - 3)

For the components affected by this subclause that declare an allocator_type, objects stored in these components shall 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’s element type, not for internal types used by the container

(23.2.1 - 7)

Unless otherwise specified, all containers defined in this clause obtain memory using an allocator

容器使用的所有内存都是由指定的分配器分配的,对不对?因为标准说内部类型不是用 allocator_traits::construct 构造的,所以应该有某种对 operator new 的调用。但是标准也说,这个条款中定义的所有容器都使用分配器获取内存,这在我看来意味着它不能是普通的 new 操作符,它必须是 placement 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 的析构函数,但在销毁 arena 对象期间释放了内存。会不会有内存泄漏,为什么?

如果能就此主题提供任何帮助,我将不胜感激。

最佳答案

分配器应该提供 4 个函数(这里很有趣):

  • 2用于内存管理:allocate/deallocate
  • 2用于对象生命周期管理:construct/destroy

您引用中的这些函数仅适用于constructdestroy(在上一句中提到),不适用于allocate/deallocate,因此不矛盾。

现在,关于内存泄漏,要让 arena 分配器工作,不仅容器中的对象应该使用 arena 分配器(容器保证)构建,而且那些对象分配的所有内存都应该也可以从此分配器获得;不幸的是,这可能会变得稍微复杂一些。

关于c++ - 容器使用的内部类型的内存分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14674289/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com