在scoped_allocator_adaptor的最小示例中使用的segfault与unordered_map的向量一起使用

时间:2018-06-06 13:30:07

标签: c++ c++11 gcc std allocator

我最近在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结合使用,如我在互联网上的一些地方所建议的那样。

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

开头的段错误
*** Error in `./tmpalloc': double free or corruption (fasttop): 0x0000000000ae3c90 ***

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

我做错了什么?

1 个答案:

答案 0 :(得分:1)

您的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之前,只需转发到单个参数版本(这是默认的两个参数operator delete将执行的操作):

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