使用基于动态/状态的分配器的STL实现?

时间:2011-02-04 11:14:39

标签: c++ stl allocator

是否有人知道STL实现允许在使用之前将动态分配器传递给容器的实例。

场景是我们有一个通用的内存分配器来管理大量的内存池,对于我们想要从不同的内存池分配每个实例的stl :: vector的每个实例。

标准STL实现的问题在于,您只能在类型基础上定义内存池,即int类型的所有向量都将从同一个池中分配。

我已经将我们的默认stl :: allocator换成了一个状态,即我们想要分配这个实例的池,但这对于stl :: list来说效果不好,它在那里分配东西默认ctor。

由于与我们的库相关的原因,我们在ctor中没有所有对象的有效池,因此我们想在用户使用stl容器之前调用'set memory pool'函数。

有没有人遇到过支持此类事情的实现?

5 个答案:

答案 0 :(得分:4)

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

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

C ++ 0x标准实际上删除了这个限制并允许将状态完全分配器传递给STL容器,只要它们是 Allocator Aware 容器(我认为它涵盖了随包装的所有容器) STL,因为他们模拟序列)。

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

答案 1 :(得分: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()强制返回连续的缓冲区(实际上vector需要它们)。

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

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

答案 2 :(得分:1)

  

标准STL的问题   实现是你只能   基于类型定义内存池   即int类型的所有向量   从同一个池分配。

这不完全正确。您可以使用不同的向量来保存int元素,每个元素具有不同的分配器类型。

然而,关于这个问题 -

  

有人知道STL吗?   允许动态的实现   要传递给的分配器   使用前容器的实例。

- C ++标准库(STL)根本不支持它,因此,虽然可能存在每个对象分配器工作的实现,但它不可移植。

有关使用自定义分配器的原因和方法的详细分析,请参阅Scott Meyers's book Effective STL,特别是第11项:了解自定义分配器的合法使用

答案 3 :(得分: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必须实现的API描述为here

不幸的是,由于STL API的限制,这可以在没有重新实现STL的情况下获得。但是,可能有第三方库允许您将分配器传递给对象的构造函数。

答案 4 :(得分:0)

好吧所以STL端口似乎确实支持这种功能,其中Microsoft(VS 2008)和GNU实现(stl大约gcc 3.4.1的端口)没有,因为它们在ctors中分配/解除分配/ 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;
}