与unordered_map向量一起使用的最小示例中的scoped_allocator_adaptor错误

Segfault in minimal example of scoped_allocator_adaptor used with vector of unordered_map

本文关键字:scoped allocator 错误 adaptor 向量 map 一起 unordered      更新时间:2023-10-16

我最近在 C++11 中了解了自定义分配器,并尝试在我的应用程序中使用它们。但是我遇到了一个段错误,可以通过以下最小示例重现:

#include <vector>
#include <unordered_map>
#include <scoped_allocator>
template<class T> struct custom_allocator {
typedef T value_type;
custom_allocator() noexcept {}
template <class U> custom_allocator (const custom_allocator<U>&) noexcept {}
T* allocate (std::size_t n) { return static_cast<T*>(::operator new(n*sizeof(T))); }
void deallocate (T* p, std::size_t n) { ::delete(p); }
template <typename U> constexpr bool operator==(const custom_allocator<U>&) const { return true; }
template <typename U> constexpr bool operator!=(const custom_allocator<U>&) const { return false; }
};
template<class T> using custom_scoped_allocator = std::scoped_allocator_adaptor<custom_allocator<T> >;
typedef std::unordered_map<int, int, std::hash<int>, std::equal_to<int>,
custom_scoped_allocator<std::pair<const int, int> > > MyMap;
typedef std::vector<MyMap, custom_scoped_allocator<MyMap> > MyVector;
int main() {
MyVector vec(1);
vec[0][0] = 0;
return 0;
}

自定义分配器正是示例中建议的分配器 http://www.cplusplus.com/reference/memory/allocator_traits/然后,它与std::scoped_allocator_adaptor相结合,正如我在互联网上的几个地方看到的那样。

代码使用 gcc 5.4.0 使用g++ -g3 -O0 -std=c++11 -march=core-avx-i -o tmpalloc tmpalloc.cpp编译良好。当我尝试运行它时,报告

*** Error in `./tmpalloc': double free or corruption (fasttop): 0x0000000000ae3c90 ***

如果我使用地址清理器重新编译,我会得到有关双重释放的以下详细信息:https://pastebin.com/raw/xy2NQtD0 (对不起,糊状物,但它并不漂亮。 基本上,unordered_map对象的析构函数似乎被调用了两次。

我做错了什么?

allocate函数分配一堆字节,但不调用任何构造函数(通过直接调用operator new全局函数(。deallocate函数在类型化指针上调用delete,这会导致在调用全局operator delete函数以释放内存之前调用该类型的析构函数。 最终结果是你破坏了你没有构造的内存(构造和销毁由调用者在调用allocate/deallocate函数之后/之前处理。

您应该使用与分配内存的方式互补的方法释放内存。 在 C++14 或更高版本中,这将是:

void deallocate (T* p, std::size_t n) { ::operator delete(p, n * sizeof(T)); }

其中n是传递给allocate的值,第二个参数是传递给new的值。 在 C++14 之前,只需中继到单参数版本(这是默认的双参数运算符 delete 将执行的操作(:

void deallocate (T* p, std::size_t n) { ::operator delete(p); }