6

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

场景是我们有一个通用的内存分配器,它管理多个内存池,并且对于 stl::vector 的每个实例,我们希望从不同的内存池中分配每个实例。

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

我已经将我们的默认 stl::allocator 换成了一个状态,即我们想要从中分配这个实例的池,但这不适用于 stl::list ,它在默认 ctor 中分​​配东西。

由于与我们的库相关的原因,我们在 ctor 中也没有针对所有对象的有效池,因此我们希望在用户可以使用 stl 容器之前调用“设置内存池”函数。

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

4

5 回答 5

4

我不太确定你的问题。因此,我将介绍状态全分配器的情况。

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

C++0x 标准实际上消除了这个限制,并允许将状态完整的分配器传递给 STL 容器,只要它们是Allocator Aware容器(我认为它涵盖了所有使用 STL 打包的容器,因为它们对Sequence进行建模)。

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

于 2011-02-04T12:19:34.377 回答
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 来实现这一点)。

于 2011-02-04T11:28:38.767 回答
1

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

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

但是,关于这个问题——

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

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

有关为什么以及如何使用自定义分配器的详细分析,请参阅Scott Meyers 的Effective STL,特别是第 11 条:了解自定义分配器的合法使用

于 2011-02-04T12:09:29.803 回答
0

好的,所以 STL 端口似乎确实支持这种功能,而 Microsoft(VS 2008)和 GNU 实现(stl circa 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;
}
于 2011-02-05T13:31:52.233 回答
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 。

不幸的是,由于 STL API 的限制,这在不重新实现 STL 的情况下已经达到了最好的水平。但是,可能有第三方库可以让您将分配器传递给对象的构造函数。

于 2011-02-04T12:00:50.230 回答