【问题标题】:An STL implementation that uses a dynamic/state based allocator?使用基于动态/状态的分配器的 STL 实现?
【发布时间】:2011-06-21 07:22:49
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

  • 是Microsoft STL在其构造函数中分配列表的头节点吗?理想的 STL 实现(阅读 GNU)在构造空容器时不会使用任何内存分配。
  • 是的,微软和我的 GNU 端口(大约 gcc 3.4.1)都在 ctor 中分​​配头节点。另一方面,STLPort 没有,因此这支持我的要求,请参阅完整示例源的答案。

标签: c++ stl allocator


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案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 来实现这一点)。

    【讨论】:

    • 请记住,如果您想要同一个分配器的多个不同实例,并且在编译时在它们之间切换就足够了,您可以将整数或标记类型参数传递给模板化分配器好吧。
    【解决方案3】:

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

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

    但是,关于这个问题——

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

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

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

    【讨论】:

      【解决方案4】:

      一种选择是使用线程局部变量来保存指向要使用的内存池的指针,并在分配器实现中捕获它。例如(使用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 的情况下,这已经是最好的了。但是,可能有第三方库可以让您将分配器传递给对象的构造函数。

      【讨论】:

        【解决方案5】:

        好的,所以 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;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-08-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-17
          • 1970-01-01
          • 2011-12-24
          相关资源
          最近更新 更多