2014-06-25 40 views
0

在std :: map中,當第一個對象被構造時,這最終導致錯誤。我檢查了調試器,我發現free_list :: init()正確地創建了連續的內存地址。我知道這個分配器不能用於向量或其他相關的容器,但它只能用於結節容器。更多與游泳池分配器與自由列表的棋局

我得到一個運行時錯誤從此在xutility(在VC12),在管線158:

_Container_proxy *_Parent_proxy = _Parent->_Myproxy; 

檢查調試器,似乎_Parent從未初始化,帶來了0000005運行時間錯誤。爲什麼或者它沒有被初始化,以及爲什麼當第一個對象被構建時(在std :: map做了3個單獨的分配之後),我不知道。

我想有性病::地圖和std ::列表和其他結節容器這項工作,我不擔心它是否能在的std ::向量執行等

#include <algorithm> 

class free_list { 
public: 
    free_list() {} 

    free_list(free_list&& other) 
     : m_next(other.m_next) { 
     other.m_next = nullptr; 
    } 

    free_list(void* data, std::size_t num_elements, std::size_t element_size) { 
     init(data, num_elements, element_size); 
    } 

    free_list& operator=(free_list&& other) { 
     m_next = other.m_next; 
     other.m_next = nullptr; 
    } 

    void init(void* data, std::size_t num_elements, std::size_t element_size) { 
     union building { 
      void* as_void; 
      char* as_char; 
      free_list* as_self; 
     }; 

     building b; 

     b.as_void = data; 
     m_next = b.as_self; 
     b.as_char += element_size; 

     free_list* runner = m_next; 
     for (std::size_t s = 1; s < num_elements; ++s) { 
      runner->m_next = b.as_self; 
      runner = runner->m_next; 
      b.as_char += element_size; 
     } 
     runner->m_next = nullptr; 
    } 

    free_list* obtain() { 
     if (m_next == nullptr) { 
      return nullptr; 
     } 
     free_list* head = m_next; 
     m_next = head->m_next; 
     return head; 
    } 

    void give_back(free_list* ptr) { 
     ptr->m_next = m_next; 
     m_next = ptr; 
    } 

    free_list* m_next; 

}; 

template<class T> 
class pool_alloc { 
    typedef pool_alloc<T> myt; 

public: 
    typedef std::size_t size_type; 
    typedef std::ptrdiff_t difference_type; 
    typedef T value_type; 
    typedef T& reference; 
    typedef const T& const_reference; 
    typedef T* pointer; 
    typedef const T* const_pointer; 

    typedef std::false_type propagate_on_container_copy_assignment; 
    typedef std::true_type propagate_on_container_move_assignment; 
    typedef std::true_type propagate_on_container_swap; 

    template<class U> struct rebind { 
     typedef pool_alloc<U> other; 
    }; 

    ~pool_alloc() { 
     destroy(); 
    } 
    pool_alloc() : data(nullptr), fl(), capacity(4096) { 
    } 
    pool_alloc(size_type capacity) : data(nullptr), fl(), capacity(capacity) {} 

    pool_alloc(const myt& other) 
     : data(nullptr), fl(), capacity(other.capacity) {} 

    pool_alloc(myt&& other) 
     : data(other.data), fl(std::move(other.fl)), capacity(other.capacity) { 
     other.data = nullptr; 
    } 

    template<class U> 
    pool_alloc(const pool_alloc<U>& other) 
     : data(nullptr), fl(), capacity(other.max_size()) {} 

    myt& operator=(const myt& other) { 
     destroy(); 
     capacity = other.capacity; 
    } 

    myt& operator=(myt&& other) { 
     destroy(); 
     data = other.data; 
     other.data = nullptr; 
     capacity = other.capacity; 
     fl = std::move(other.fl); 
    } 

    static pointer address(reference ref) { 
     return &ref; 
    } 

    static const_pointer address(const_reference ref) { 
     return &ref; 
    } 

    size_type max_size() const { 
     return capacity; 
    } 

    pointer allocate(size_type) { 
     if (data == nullptr) create(); 
     return reinterpret_cast<pointer>(fl.obtain()); 
    } 

    void deallocate(pointer ptr, size_type) { 
     fl.give_back(reinterpret_cast<free_list*>(ptr)); 
    } 

    template<class... Args> 
    static void construct(pointer ptr, Args&&... args) { 
     ::new (ptr) T(std::forward<Args>(args)...); 
    } 

    static void destroy(pointer ptr) { 
     ptr->~T(); 
    } 

    bool operator==(const myt& other) const { 
     return reinterpret_cast<char*>(data) == 
      reinterpret_cast<char*>(other.data); 
    } 

    bool operator!=(const myt& other) const { 
     return !operator==(other); 
    } 

private: 

    void create() { 
     data = ::operator new(capacity * sizeof(value_type)); 
     fl.init(data, capacity, sizeof(value_type)); 
    } 

    void destroy() { 
     ::operator delete(data); 
     data = nullptr; 
    } 

    void* data; 
    free_list fl; 
    size_type capacity; 

}; 

template<> 
class pool_alloc <void> { 
public: 
    template <class U> struct rebind { typedef pool_alloc<U> other; }; 

    typedef void* pointer; 
    typedef const void* const_pointer; 
    typedef void value_type; 
}; 

問題是當被構建的std ::對(在MSVC12效用在線路214):

template<class _Other1, 
     class _Other2, 
     class = typename enable_if<is_convertible<_Other1, _Ty1>::value 
      && is_convertible<_Other2, _Ty2>::value, 
      void>::type> 
     pair(_Other1&& _Val1, _Other2&& _Val2) 
      _NOEXCEPT_OP((is_nothrow_constructible<_Ty1, _Other1&&>::value 
       && is_nothrow_constructible<_Ty2, _Other2&&>::value)) 
     : first(_STD forward<_Other1>(_Val1)), 
       second(_STD forward<_Other2>(_Val2)) 
     { // construct from moved values 
     } 

即使在步進後,發生運行時錯誤,上面_Parent未被初始化的描述相同。

回答

0

我能夠通過廣泛的調試來回答我自己的問題。顯然,VC12的的std ::地圖至少有時實施將強制轉換停留在範圍爲地圖,這是用來在地圖分配和釋放的節點的壽命_Alnod(永久分配器,我會想到什麼實際調用分配()DEALLOCATE())作爲_Alproxy,它可以創建某種物體稱爲_Mproxy(或類似的東西)的臨時分配分配()。但問題在於,VC12的實現會讓_Alproxy超出範圍,但仍然期望指向分配對象的指針保持有效,因此很明顯,我將不得不使用:: operator new:: operator delete對像_Mproxy對象:使用一個內存池,然後超出範圍,而指向其中的位置指針仍然是導致崩潰的原因。

我想出了什麼,我想,這個被稱爲骯髒的伎倆,那就是當複製建造或複製分配一個分配器到另一個分配器類型進行測試:

template<class U> 
pool_alloc(const pool_alloc<U>& other) 
    : data(nullptr), fl(), capacity(other.max_size()), use_data(true) { 
    if (sizeof(T) < sizeof(U)) use_data = false; 
} 

我增加了布爾成員use_data到分配器類,如果真正意味着使用內存池,並且如果意味着使用::運營商新:: delete操作符。默認情況下,它是true。當分配器被轉換爲模板參數大小小於源分配器大小的另一個分配器類型時,它的值的問題就出現了;在這種情況下,use_data設置爲false。由於此對象或任何它被稱爲相當小,此修復程序似乎工作,即使使用std ::設置char作爲元素類型。

我測試過這一點使用的std ::設置在兩個VC12型焦炭和GCC 4.8.1在32位和發現,在這兩種情況下,它的工作原理。在兩種情況下分配和解除分配節點時,都會使用內存池。

下面是完整的源代碼:

#include <algorithm> 

class free_list { 
public: 

    free_list() {} 

    free_list(free_list&& other) 
     : m_next(other.m_next) { 
     other.m_next = nullptr; 
    } 

    free_list(void* data, std::size_t num_elements, std::size_t element_size) { 
     init(data, num_elements, element_size); 
    } 

    free_list& operator=(free_list&& other) { 
     if (this != &other) { 
      m_next = other.m_next; 
      other.m_next = nullptr; 
     } 
     return *this; 
    } 

    void init(void* data, std::size_t num_elements, std::size_t element_size) { 
     union building { 
      void* as_void; 
      char* as_char; 
      free_list* as_self; 
     }; 

     building b; 

     b.as_void = data; 
     m_next = b.as_self; 
     b.as_char += element_size; 

     free_list* runner = m_next; 
     for (std::size_t s = 1; s < num_elements; ++s) { 
      runner->m_next = b.as_self; 
      runner = runner->m_next; 
      b.as_char += element_size; 
     } 
     runner->m_next = nullptr; 
    } 

    free_list* obtain() { 
     if (m_next == nullptr) { 
      return nullptr; 
     } 
     free_list* head = m_next; 
     m_next = head->m_next; 
     return head; 
    } 

    void give_back(free_list* ptr) { 
     ptr->m_next = m_next; 
     m_next = ptr; 
    } 

    free_list* m_next; 

}; 

template<class T> 
class pool_alloc { 
    typedef pool_alloc<T> myt; 

public: 
    typedef std::size_t size_type; 
    typedef std::ptrdiff_t difference_type; 
    typedef T value_type; 
    typedef T& reference; 
    typedef const T& const_reference; 
    typedef T* pointer; 
    typedef const T* const_pointer; 

    typedef std::false_type propagate_on_container_copy_assignment; 
    typedef std::true_type propagate_on_container_move_assignment; 
    typedef std::true_type propagate_on_container_swap; 

    myt select_on_container_copy_construction() const { 
     return *this; 
    } 

    template<class U> struct rebind { 
     typedef pool_alloc<U> other; 
    }; 

    ~pool_alloc() { 
     clear(); 
    } 
    pool_alloc() : data(nullptr), fl(), capacity(4096), use_data(true) {} 

    pool_alloc(size_type capacity) : data(nullptr), fl(), 
     capacity(capacity), use_data(true) {} 

    pool_alloc(const myt& other) 
     : data(nullptr), fl(), capacity(other.capacity), 
      use_data(other.use_data) {} 

    pool_alloc(myt&& other) 
     : data(other.data), fl(std::move(other.fl)), capacity(other.capacity), 
      use_data(other.use_data) { 
     other.data = nullptr; 
    } 

    template<class U> 
    pool_alloc(const pool_alloc<U>& other) 
     : data(nullptr), fl(), capacity(other.max_size()), use_data(true) { 
     if (sizeof(T) < sizeof(U)) use_data = false; 
    } 


    myt& operator=(const myt& other) { 
     if (*this != other) { 
      clear(); 
      capacity = other.capacity; 
      use_data = other.use_data; 
     } 
    } 

    myt& operator=(myt&& other) { 
     if (*this != other) { 
      clear(); 
      data = other.data; 
      other.data = nullptr; 
      capacity = other.capacity; 
      use_data = other.use_data; 
      fl = std::move(other.fl); 
     } 
     return *this; 
    } 

    template<class U> 
    myt& operator=(const pool_alloc<U>& other) { 
     if (this != reinterpret_cast<myt*>(&other)) { 
      capacity = other.max_size(); 
      if (sizeof(T) < sizeof(U)) 
       use_data = false; 
      else 
       use_data = true; 
     } 
     return *this; 
    } 

    static pointer address(reference ref) { 
     return &ref; 
    } 

    static const_pointer address(const_reference ref) { 
     return &ref; 
    } 

    size_type max_size() const { 
     return capacity; 
    } 

    pointer allocate(size_type) { 
     if (use_data) { 
      if (data == nullptr) create(); 
      return reinterpret_cast<pointer>(fl.obtain()); 
     } else { 
      return reinterpret_cast<pointer>(::operator new(sizeof(T))); 
     } 
    } 

    void deallocate(pointer ptr, size_type) { 
     if (use_data) { 
      fl.give_back(reinterpret_cast<free_list*>(ptr)); 
     } else { 
      ::operator delete(reinterpret_cast<void*>(ptr)); 
     } 
    } 

    template<class... Args> 
    static void construct(pointer ptr, Args&&... args) { 
     ::new ((void*)ptr) value_type(std::forward<Args>(args)...); 
    } 

    static void destroy(pointer ptr) { 
     ptr->~value_type(); 
    } 

    bool operator==(const myt& other) const { 
     return reinterpret_cast<char*>(data) == 
      reinterpret_cast<char*>(other.data); 
    } 

    bool operator!=(const myt& other) const { 
     return !operator==(other); 
    } 

private: 

    void create() { 
     size_type size = sizeof(value_type) < sizeof(free_list*) ? 
      sizeof(free_list*) : sizeof(value_type); 
     data = ::operator new(capacity * size); 
     fl.init(data, capacity, size); 
    } 

    void clear() { 
     ::operator delete(data); 
     data = nullptr; 
    } 

    void* data; 
    free_list fl; 
    size_type capacity; 
    bool use_data; 
}; 

template<> 
class pool_alloc <void> { 
public: 
    template <class U> struct rebind { typedef pool_alloc<U> other; }; 

    typedef void* pointer; 
    typedef const void* const_pointer; 
    typedef void value_type; 
}; 

template<class Container, class Alloc> 
void change_capacity(Container& c, typename Alloc::size_type new_capacity) { 
    Container temp(c, Alloc(new_capacity)); 
    c = std::move(temp); 
} 

由於分配不是自動增長(不知道怎麼做出這樣的事),我已經添加了change_capacity()功能。