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

Segfault in minimal example of scoped_allocator_adaptor used with vector of unordered_map

我最近了解了 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 ***

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

我做错了什么?

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

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

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

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

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