6

使用前に動的アロケーターをコンテナーのインスタンスに渡すことができる STL 実装を知っている人はいますか?

シナリオは、多数のメモリ プールを管理する一般的なメモリ アロケータがあり、たとえば stl::vector の各インスタンスに対して、異なるメモリ プールから各インスタンスを割り当てたいというものです。

標準の STL 実装の問題は、型ベースでのみメモリ プールを定義できることです。つまり、型 int のすべてのベクトルは同じプールから割り当てられます。

私はすでにデフォルトの stl::allocator を、状態を持つもの、つまりこのインスタンスを割り当てたいプールに交換しましたが、これは、デフォルトの ctor に物を割り当てる stl::list ではうまく機能しません。

ライブラリに関連する理由により、すべてのオブジェクトに対して有効なプールが ctor にないため、ユーザーが stl コンテナーを使用できるようになる前に、「メモリ プールの設定」関数を呼び出す必要があります。

この種のことをサポートする実装に出くわした人はいますか?

4

5 に答える 5

4

あなたの質問について正確にはわかりません。そこで、ステートフル アロケーターのケースについて説明します。

C++03 では、どのアロケーターも、同じ型の別のアロケーターによって割り当てられたリソースの割り当てを解除できる必要があります。

C++0x 標準では実際にこの制限が取り除かれ、 Allocator Awareコンテナーである限り、ステートフル アロケーターを STL コンテナーに渡すことができます( Sequenceをモデル化するため、STL でパッケージ化されたすべてのコンテナーをカバーしていると思います)。

例: [allocator.adaptor] $20.10 Class scoped_allocatorC++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」、特にItem 11: カスタム アロケーターの正当な使用を理解するを参照してください。

于 2011-02-04T12:09:29.803 に答える
0

そうですね、STLポートはこの種の機能をサポートしているようですが、Microsoft(VS 2008)とGNU実装(gcc 3.4.1頃のstlのポート)は、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

1 つのオプションは、スレッド ローカル変数を使用して、使用するメモリ プールへのポインターを保持し、アロケーターの実装でこれをキャプチャすることです。例 ( 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 に答える