2011-02-04 34 views
6

有没有人知道STL实现允许在使用前将动态分配器传递到容器的实例。使用基于动态/状态的分配器的STL实现?

的情况是,我们有一个管理的若干内存池和发言权STL ::向量的每个实例,我们想从一个内存池不同的分配每个实例的通用内存分配器。

与标准STL实现的问题是,你只能定义一个基础型内存池即所有载体类型为int的将来自同一个池中分配。

我已经换掉了我们默认的stl :: allocator,它有一个状态,即我们想从这个实例分配这个实例的池,但是这对于stl :: list来说并不适用,因为它分配了一些东西默认的ctor。

对于与我们的图书馆的原因,我们还没有一个有效的池在构造函数的所有对象,所以我们想要把一个“设置内存池”功能之前,用户可以使用STL容器。

有没有人遇到过支持这种事情的实现?

+0

是在其构造函数中分配表头节点的Microsoft STL吗?理想的STL实现(读取GNU)在构造空容器时不会使用任何内存分配。 – 2011-02-04 12:55:30

+0

是的,微软和我的GNU端口(大约是gcc 3.4.1)都分配了ctor中的头节点。另一方面,STLPort不支持,所以这支持我的需求,请参阅我的答案,以获得完整的示例源代码。 – user176168 2011-02-05 13:36:48

回答

2

键入的分配器可以使用下面的通用分配器来执行分配。

分配器需要支持这些功能:

pointer address (reference x) const; 
    const_pointer address (const_reference x) const; 
    pointer allocate (size_type n, allocator<void>::const_pointer hint=0); 
    void deallocate (pointer p, size_type n); 
    size_type max_size() const throw(); 
    void construct (pointer p, const_reference val); 

假设你有一个刚分配内存和释放内存的机制,你可以用它来实现一些功能上面。

正在键入分配器的好处是,你知道你将要创建大量大小完全相同的项目,因此可以创造你的“页”,以适应。最大的问题可能是你被allocate()强制返回连续的缓冲区(并且实际上向量需要它们)。

http://www.cplusplus.com/reference/std/memory/allocator/

你的问题还是不清楚,为什么这是不够的。您可以用“一次”逻辑初始化内存池。 (如果它是多线程的,你可以使用boost :: once来实现这一点)。

0

一种选择是使用一个线程局部变量的指针保持到内存池使用,并捕捉到了这个在您的分配器实现。例如(使用boost::thread_specific_ptr):

// Global variable 
boost::thread_specific_ptr<allocator_impl> real_allocator; 

struct set_allocator : boost::noncopyable { 
private: 
    allocator_impl *old; 
public: 
    set_allocator(allocator_impl *newAllocator) { 
     old = real_allocator.get(); 
     real_allocator.reset(newAllocator); 
    } 
    ~set_allocator() { 
     real_allocator.reset(old); 
    } 
}; 

template<typename T> 
struct myallocator { 
private: 
    real_allocator *delegate; 
public: 
    myallocator() { 
     delegate = real_allocator.get(); 
    } 
    T *allocate(size_t n, allocator<void>::const_pointer hint=0) 
    { 
     return delegate->allocate(sizeof(T), n, hint); 
    } 
    // Other mandatory STL Allocator functions and typedefs follow 
}; 

// later: 
allocator_impl *allocator = new allocator_impl(); 
set_allocator sa(allocator); // Set current allocator using RAII 
std::list<int, myallocator> mylist; // using *allocator as the backing allocator 

该myallocator必须实现被描述here的API。

不幸的是,由于STL API的限制,这是一样好,你可以在不重新实现STL得到。然而,可能有第三方库允许你将分配器传递给对象的构造函数。

1

与标准STL的问题 实现的是,你只能 定义一个类型的基础上 即int类型的所有矢量的将 从同一池中分配的内存池。

这并不完全正确。您可以拥有不同的向量,每个元素具有不同的分配器类型。

然而,对于这个问题 -

有谁知道一个STL 实现,它允许在使用前一个集装箱的 实例传递动态 分配器的。

- 它根本就不是由C++标准库(STL)的支持,因此,虽然它可能是有实现中每个对象分配器的工作,它不是便携式的。

有关为何以及如何使用自定义的分配进行详细的分析见Scott Meyers'sEffective STL,特别是项目11:了解合法使用自定义的分配的。

4

我不太确定你的问题。所以我将介绍一个状态完全分配器的情况。

在C++ 03中,任何分配器都应该能够释放由相同类型的另一个分配器分配的资源。

的的C++ 0x标准实际删除此限制,并允许全状态分配器可以,只要他们是分配器意识到容器(我认为它涵盖了与STL包装所有的容器,因为它们传递给STL容器型号序列)。

例如:[allocator.adaptor] $20.10 Class scoped_allocator现在是C++ 0x STL的一部分。

0

好了,所以STL端口似乎支持这种功能,在微软(VS 2008)和GNU执行(STL大约GCC 3.4.1的端口)不要因为他们在分配/解除分配的东西构建函数/ dtors。

这里是我的测试代码,显示如何做到这一点。不要以任何方式警告它的完整实施!

#include <list> 
#include <assert.h> 

namespace my 
{ 

class memory_pool 
{ 
public: 
    memory_pool() : m_top(0){}; 
    ~memory_pool(){}; 

    void* alloc(int size, int align) { void* p = (void*)&m_pool[m_top]; m_top += size; return p; } 
    void free (void* p) { assert((p >= m_pool) && (p < &m_pool[m_top])); } 
private: 
    char m_pool[0xFFFF]; 
    int m_top; 
}; 

template<class T> 
class dynamic_allocator 
{ 
    template<typename U> friend class dynamic_allocator; 
public: 
    typedef T     value_type;   
    typedef size_t    size_type;   
    typedef value_type*   pointer;    
    template <class _Tp1> struct rebind { typedef dynamic_allocator<_Tp1> other; }; 

    dynamic_allocator() : m_pPool(NULL){} 
    dynamic_allocator(memory_pool* pPool){ m_pPool = pPool; } 
    dynamic_allocator(const dynamic_allocator<T>& alloc) : m_pPool(alloc.m_pPool){} 
    template< typename U > 
    dynamic_allocator(const dynamic_allocator<U>& alloc) : m_pPool(alloc.m_pPool){} 
    ~dynamic_allocator() {} 

    pointer allocate(size_type count){ return allocate(count, NULL); } 
    pointer allocate(size_type count, const void*) { assert(m_pPool); return (pointer)m_pPool->alloc(count * sizeof(T), __alignof(T)); } 
    void deallocate(pointer p, size_type count) { assert(m_pPool); m_pPool->free(p); } 

    void set(memory_pool* pPool) { m_pPool = pPool; } 

private: 
    memory_pool* m_pPool; 
}; 

template< typename T, typename Al = dynamic_allocator<T> > 
class list : public std::list<T, Al> 
{ 
public: 
    typedef typename std::list<T, Al>::allocator_type allocator_type; 

    list() : std::list<T, Al>(){}; 
    list(const allocator_type& a) : std::list<T, Al>(a){}; 
    ~list(){}; 

    void initialise(memory_pool& pool){ std::list<T, Al>::_M_node.set(&pool); } // or something like this 
    void terminate(void){ clear(); std::list<T, Al>::_M_node.set(NULL); }     // or something like this 
}; 

}; // namespace my 

class lemon 
{ 
public: 
    lemon(){}  // must be empty ctor as we don't want to have active mem pool in ctor for users to use 
    ~lemon(){} 

    void initialise(my::memory_pool& pool){ m_list.initialise(pool); } 
    void terminate(void)     { m_list.terminate(); } 

    void add(float f) { m_list.push_back(f); } 

private: 
    my::list<float> m_list; 
}; 

int main(void) 
{ 
    my::memory_pool poolA; 
    my::memory_pool poolB; 

    my::dynamic_allocator<float> aa(&poolA); 
    my::list<float> a(aa); 
    my::list<float> fail; 

    std::list<float>::allocator_type bb; 
    std::list<float> b(bb); 

    a.push_back(0.2f); 
    b.push_back(50.0f); 
    //fail.push_back(19.0f); 

    a.clear(); 
    b.clear(); 

    lemon lemons[2]; 

    lemons[0].initialise(poolA); 
    lemons[1].initialise(poolB); 

    lemons[0].add(10.0f); 
    lemons[1].add(20.0f); 
    lemons[1].add(18.0f); 

    lemons[0].terminate(); 
    lemons[1].terminate(); 

    scanf("press any key\n"); 

    return 0; 
}