2017-11-11 98 views
0

我製作了一個簡單的堆棧分配器,可用於在堆棧上分配小容器。 我一直在使用這個類一段時間,它一直工作正常。 但是,今天我又切換了調試迭代器(_ITERATOR_DEBUG_LEVEL=2),並且在由此開關激活的調試代碼中突然出現訪問衝突。啓用MSVC調試迭代器時堆棧分配器訪問衝突

模板編程和標準庫編碼約定的混合使得這非常難以調試,而且我也不完全是分配器方面的專家。我違反了分配器的某種規則嗎?

下面的代碼應該能夠重現錯誤。

#include <memory> 
#include <vector> 

template <typename T, size_t N, template <typename> typename Allocator = std::allocator> 
class StackAllocator : public Allocator<T> 
{ 
    public: 
    using base   = Allocator<T>; 
    using pointer_type = typename base::pointer; 
    using size_type = typename base::size_type; 

    StackAllocator() noexcept = default; 
    StackAllocator(const StackAllocator& a_StackAllocator) noexcept = default; 

    template <typename U> 
    StackAllocator(const StackAllocator<U, N, Allocator>& a_StackAllocator) noexcept 
    { 
    } 

    pointer_type allocate(size_type a_Size, void* a_Hint = nullptr) 
    { 
     if (!m_Active && a_Size <= N) 
     { 
      m_Active = true; 
      return GetStackPointer(); 
     } 
     else 
     { 
      return base::allocate(a_Size, a_Hint); 
     } 
    } 

    void deallocate(pointer_type a_Pointer, size_type a_Size) 
    { 
     if (a_Pointer == GetStackPointer()) 
     { 
      m_Active = false; 
     } 
     else 
     { 
      base::deallocate(a_Pointer, a_Size); 
     } 
    } 

    template <class U> 
    struct rebind 
    { 
     using other = StackAllocator<U, N, Allocator>; 
    }; 

    private: 
    pointer_type GetStackPointer() 
    { 
     return reinterpret_cast<pointer_type>(m_Data); 
    } 

    std::aligned_storage_t<sizeof(T), alignof(T)> m_Data[N]; 
    bool m_Active = false; 
}; 

template <typename T, size_t N> 
class StackVector : public std::vector<T, StackAllocator<T, N>> 
{ 
    public: 
    using allocator_type = StackAllocator<T, N>; 
    using base   = std::vector<T, allocator_type>; 

    StackVector() noexcept(noexcept(allocator_type())): 
     StackVector(allocator_type()) 
    { 
    } 

    explicit StackVector(const allocator_type& a_Allocator) noexcept(noexcept(base(a_Allocator))): 
     base(a_Allocator) 
    { 
     base::reserve(N); 
    } 

    using base::vector; 
}; 

int main(int argc, char* argv[]) 
{ 
    StackVector<size_t, 1> v; 

    return v.capacity(); 
} 

我正在使用MSVC 2017(15.4.0)。

+3

該標準要求從分配器'a'構造的分配器'b'拷貝 - 等於'a';這又意味着'a'分配的內存可以被'b'解除分配。你的班級違反了這個要求,因此不是一個有效的分配器。實際上,你繼承了'std :: allocator :: operator ==',它總是返回'true';但是你的班級實例是非常不可互換的。 –

+1

原來的答案是downvoted,但主要的一點是你不能在分配器中存儲,因爲它們是無狀態的類價值類型。我什至不知道你會如何支持相同類型的多個這樣的分配器。另外,您必須努力確保分配仍在使用中時存儲不會超出範圍。例如。在存儲被破壞之前調用斷言釋放。 –

回答

0

這個問題確實違反了@Igor所說的要求。我通過在分配器之外存儲堆棧數據解決了這個問題。現在,StackVector是數據的所有者,分配器及其副本都指向該內存,從而確保由一個分配器分配的內存可以由該分配器的副本釋放。

該解決方案確實會給用戶帶來一點複雜性(現在必須管理2個對象而不是1個),但在分配器需求內部似乎沒有其他方法,並且可以通過包裝容器也是如此。

該解決方案的一個很好的例子可以在Chromium's StackAllocator中找到。