繁体   English   中英

自定义 C++ 分配器导致错误:_Container_base12::_Orphan_all_unlocked_v3()

[英]Custom C++ Allocator cause error in : _Container_base12::_Orphan_all_unlocked_v3()

所以我打算将这个基于堆栈的分配器用于 std::vector,并且我使用 2 arrays 进行分配(因为向量增长并将旧缓冲区复制到新缓冲区)。

这是完整的代码:

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <vector>
#include <cstddef>
#include <cassert>
#include <array>

using size_t = std::size_t;
using byte = std::byte;

template <class T, size_t capacity = 512>
class stack_allocator
{
public:
    using value_type = T;

    using pointer = value_type*;
    using const_pointer = typename std::pointer_traits<pointer>::template rebind<value_type const>;
    using void_pointer = typename std::pointer_traits<pointer>::template rebind<void>;
    using const_void_pointer = typename std::pointer_traits<pointer>::template rebind<const void>;

    using difference_type = typename std::pointer_traits<pointer>::difference_type;
    using size_type = std::make_unsigned_t<difference_type>;

    template <class U> struct rebind { typedef stack_allocator<U, capacity> other; };

    stack_allocator() noexcept {};  // not required, unless used
    ~stack_allocator() noexcept = default;

    //stack_allocator(stack_allocator&&) = delete;
    //stack_allocator& operator=(stack_allocator&&) = delete;
    //stack_allocator(const stack_allocator&) = delete;
    //stack_allocator& operator=(const stack_allocator&) = delete;

    template <class U>
    stack_allocator(stack_allocator<U> const&) noexcept {}
    // ? is n already aligned ?
    inline pointer allocate(size_t n)
    {
        constexpr auto max_size_allowed = (capacity>>1);
        auto size = n * sizeof(value_type); 
        if (size > max_size_allowed)
        {
            return static_cast<pointer>(::operator new (size));
        }
        else
        {
            m_Index = !m_Index;
            return static_cast<pointer>(static_cast<void*>(&m_Array[static_cast<size_t>(m_Index)][0]));
        }
    }

    inline void deallocate(pointer p, size_t n) noexcept
    {
        constexpr auto max_size_allowed = (capacity>>1);
        auto size = n * sizeof(value_type);
        if (size > max_size_allowed)
            ::operator delete(p);
        else
        {
            // do nothing
        }
    }

    inline pointer allocate(size_t n, const_void_pointer)
    {
        return allocate(n);
    }

    template <class U, class ...Args>
    void construct(U* p, Args&& ...args)
    {
        ::new(p) U(std::forward<Args>(args)...);
    }

    template <class U>
    void destroy(U* p) noexcept
    {
        p->~U();
    }

    inline constexpr size_t max_size() const noexcept
    {
        return std::numeric_limits<std::size_t>::max() / sizeof(T);
    }

    stack_allocator select_on_container_copy_construction() const
    {
        return *this;
    }

    using propagate_on_container_copy_assignment = std::true_type;
    using propagate_on_container_move_assignment = std::false_type;
    using propagate_on_container_swap = std::true_type;
    using is_always_equal = std::true_type;

protected:
    //const bool is_pointer_in_range(byte* p) const noexcept { return (p >= &m_Array[0]) && (p <= &m_Array[capacity - 1]); }

    bool m_Index{ false };
    byte m_Array[2][(capacity >> 1)]{ {static_cast<byte>(0)} };
};

template <class T, size_t capacity, class U>
bool operator==(stack_allocator<T, capacity> const&, stack_allocator<U, capacity> const&) noexcept
{
    return true;
}

template <class T, size_t capacity, class U>
bool operator!=(stack_allocator<T, capacity> const& x, stack_allocator<U, capacity> const& y) noexcept
{
    return false;
}

int main(int argc, char** argv)
{
    std::vector<int, stack_allocator<int, 512>> stackVec;
    stackVec.push_back(1);
    stackVec.push_back(2);
    stackVec.push_back(3);

    return 0;
}

代码以某种方式导致崩溃:在此处输入图像描述

在此处输入图像描述

我注意到“_Container_base12”继承自分配器,所以我想我的“stack_allocator”实现一定有问题。 但我想不通。

希望知道编写分配器的正确方法是什么。

多亏了Igor Tandetnik ,我才能够找出问题所在。 经过一番研究,我也发现,在 C++17 中其实很容易做到这一点,我们可以这样做:

std::array<unsigned char, 64> memory;
std::pmr::monotonic_buffer_resource pool{ memory.data(), memory.size() };

// then we can define our vector using stack memory
std::vector<int, std::pmr::polymorphic_allocator<int>> vec{&pool};

或者,我们可以将“std::array<unsigned char, 64> memory”放入自定义 memory_resource 中。

就我而言,我想对小对象使用就地数组以避免“分配”。 对于大对象,我想使用 pooled memory。

所以我继承了 _Identity_equal_resource 并在后台使用了一个全局的 unsynchronized_pool_resource。

现在一切对我来说都很完美。

// Note: still testing this.
template<size_t local_arena_size>
struct LocalArenaMemoryResource : public std::pmr::_Identity_equal_resource
{
private:
    static inline constexpr size_t max_blocks_per_chunk = 16;
    static inline constexpr size_t largest_required_pool_block = 16 * 1024;
    static inline std::pmr::unsynchronized_pool_resource _pool{ std::pmr::pool_options { max_blocks_per_chunk, largest_required_pool_block } };
private:
    using base = std::pmr::unsynchronized_pool_resource;
    void* do_allocate(size_t _Bytes, size_t _Align) override
    {
        if (_Bytes <= local_arena_size)
        {
            return static_cast<void*>(&m_local_buffer[0]);
        }

        return _pool.allocate(_Bytes, _Align);
    }

    void do_deallocate(void* _Ptr, size_t _Bytes, size_t _Align) override
    {
        if (_Ptr >= &m_local_buffer[0] && _Ptr < &m_local_buffer[local_arena_size - 1])
        {
            // do nothing
        }
        else
        {
            _pool.deallocate(_Ptr, _Bytes, _Align);
        }
    }

private:
    std::array<byte, local_arena_size> m_local_buffer;
};

有些事情我仍在学习,例如:当需要超大分配时,unsynchronized_pool_resource 如何处理分配。 另外我想知道 fixed_block_pool 是否会更有效率。 仍在学习,测试,但它真的很有趣。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM