STL stack allocate

游戏编程精粹3提供了一份栈分配器源代码:

#include <memory>

#include <limits>



template <typename T>

class StackAlloc

{

public:



    // Typedefs

    typedef size_t    size_type;

    typedef ptrdiff_t difference_type;

    typedef T*        pointer;

    typedef const T*  const_pointer;

    typedef T&        reference;

    typedef const T&  const_reference;

    typedef T         value_type;



    // Constructors

    StackAlloc() throw()

    :

        mpStack( NULL ),

        mBytesAllocated( 0 ),

        mMaxBytes( 0 )

    {

    }



    StackAlloc( unsigned char* pStack, size_t nMaxBytes ) throw()

    :

        mpStack( pStack ),

        mBytesAllocated( 0 ),

        mMaxBytes( nMaxBytes )

    {

    }



    StackAlloc( const StackAlloc& sa ) throw()

    :

        mpStack( sa.mpStack ),

        mBytesAllocated( 0 ),

        mMaxBytes( sa.mMaxBytes )

    {

        // Copying the "stack" resets mBytesAllocated to zero

    }



#if _MSC_VER >= 1400 // VC 7 can't handle template members

    template <typename U>

    StackAlloc( const StackAlloc<U>& sa ) throw()

    :

        mpStack( sa.mpStack ),

        mBytesAllocated( 0 ),

        mMaxBytes( sa.mMaxBytes )

    {

        // Copying the "stack" resets mBytesAllocated to zero

    }

#endif



    StackAlloc& operator=( const StackAlloc& sa )

    {

        // Copying the "stack" resets mBytesAllocated to zero

        mpStack = sa.mpStack;

        mBytesAllocated = 0;

        mMaxBytes = sa.mMaxBytes;

        return *this;

    }



    // Destructor

    ~StackAlloc() throw()

    {

    }



    // Utility functions

    pointer address( reference r ) const

    {

        return &r;

    }



    const_pointer address( const_reference c ) const

    {

        return &c;

    }



    size_type max_size() const

    {

        return std::numeric_limits<size_t>::max() / sizeof(T);

    }



    // In-place construction

    void construct( pointer p, const_reference c )

    {

        // placement new operator

        new( reinterpret_cast<void*>(p) ) T(c);

    }



    // In-place destruction

    void destroy( pointer p )

    {

        // call destructor directly

        (p)->~T();

    }



    // Rebind to allocators of other types

    template <typename U>

    struct rebind

    {

        typedef StackAlloc<U> other;

    };



    // Allocate raw memory

    pointer allocate( size_type n, const void* = NULL )

    {

        void* pRaw = mpStack + mBytesAllocated;

        mBytesAllocated += ( n * sizeof(T) );



        if( mBytesAllocated+1 > mMaxBytes )

            throw std::bad_alloc();



        return pointer(pRaw);

    }



    // Free raw memory.

    // Note that C++ standard defines this function as

    // deallocate( pointer p, size_type). Because Visual C++ 6.0

    // compiler doesn't support template rebind, Dinkumware uses

    // void* hack.

    void deallocate( void*, size_type )

    {

        // No need to free stack memory

    }



    // Non-standard Dinkumware hack for Visual C++ 6.0 compiler.

    // VC 6 doesn't support template rebind.

    char* _Charalloc( size_type n )

    {

        return reinterpret_cast<char*>( allocate( n, NULL ) );

    }



    // Required for global comparison functions

    unsigned char* GetStack() const

    {

        return mpStack;

    }



private:



    unsigned char* mpStack;

    size_t         mBytesAllocated;

    size_t         mMaxBytes;



}; // end of StackAlloc



// Comparison

template <typename T1>

bool operator==( const StackAlloc<T1>& lhs, const StackAlloc<T1>& rhs) throw()

{

    return lhs.GetStack() == rhs.GetStack();

}



template <typename T1>

bool operator!=( const StackAlloc<T1>& lhs, const StackAlloc<T1>& rhs) throw()

{

    return lhs.GetStack() != rhs.GetStack();

}
View Code

测试发现在VS2012下编译运行,栈分配器在释放内存时出错。

原因是当用户提供自定义的分配器时,VS会保存一个分配策略对象来管理计数神马的。
部分VS源代码如下:

    _Vector_alloc(const _Alty& _Al = _Alty())

        : _Alval(_Al)

        {    // construct allocator from _Al

        _Alloc_proxy();

        }

//-------------------------------------------------------------

    void _Alloc_proxy()

        {    // construct proxy from _Alval

        typename _Alloc::template rebind<_Container_proxy>::other

            _Alproxy(_Alval);

        this->_Myproxy = _Alproxy.allocate(1);

        _Alproxy.construct(this->_Myproxy, _Container_proxy());

        this->_Myproxy->_Mycont = this;

        }
View Code

vector的栈分配器是_Alval,也就是用户提供的栈分配器。

分配策略对象_Myproxy占用的内存也是通过_Alval申请的。仅仅是这样也许不会出现神马问题,可是_Myproxy是通过调用template <typename U> StackAlloc( const StackAlloc<U>& sa )重绑定函数从_Alval获取了一个新的分配器对象_Alproxy。此时_Alval与_Alproxy其实是共用一段内存的,而彼此不知道对方的存在。导致_Myproxy使用的内存在vector插入元素时被覆盖。当vector释放内存或者迁移内存时需要释放掉_Myproxy的内存时程序就崩溃了。

实际上游戏编程精粹3提供的stack allocate在语义上就存在问题,mpstack不应该是值语义的,因为stack allocate实际上即没有真正向系统申请内存,也并没有真正的释放内存,而只是代为管理一段原本就存在的内存。因此,mpstack应该是引用语义的。

现将本人修改后的代码献上:

PS:只在VS2012下测试了vector与list容器。

  1         template<typename T>

  2         class stack_alloc

  3         {

  4         public:

  5             typedef size_t    size_type;

  6             typedef ptrdiff_t difference_type;

  7             typedef T*        pointer;

  8             typedef const T*  const_pointer;

  9             typedef T&        reference;

 10             typedef const T&  const_reference;

 11             typedef T         value_type;

 12 

 13         public:

 14             stack_alloc() throw()

 15                 : m_begin(NULL)

 16                 , m_cur(m_begin)

 17                 , m_max_bytes(0)

 18             {

 19             }

 20 

 21             stack_alloc(uchar* pstack, size_t max_bytes) throw()

 22                 : m_begin(pstack)

 23                 , m_cur(m_begin)

 24                 , m_max_bytes(max_bytes)

 25             {

 26             }

 27 

 28             stack_alloc(const stack_alloc& sa) throw()

 29                 : m_begin(sa.m_begin)

 30                 , m_cur(sa.m_cur)

 31                 , m_max_bytes(sa.m_max_bytes)

 32             {

 33             }

 34 

 35 #if _MSC_VER >= 1400 // VC 7 can't handle template members

 36             template <typename U>

 37             stack_alloc(const stack_alloc<U>& sa) throw()

 38                 : m_begin(sa.m_begin)

 39                 , m_cur(sa.m_cur)

 40                 , m_max_bytes(sa.m_max_bytes)

 41             {

 42             }

 43 #endif

 44 

 45             stack_alloc& operator=( const stack_alloc& rhs )

 46             {

 47                 return *this;

 48             }

 49 

 50             ~stack_alloc() throw()

 51             {

 52             }

 53 

 54         public:

 55             // Utility functions

 56             pointer address( reference r ) const

 57             {

 58                 return &r;

 59             }

 60 

 61             const_pointer address( const_reference c ) const

 62             {

 63                 return &c;

 64             }

 65 

 66             size_type max_size() const

 67             {

 68                 return m_max_bytes/sizeof(T);

 69             }

 70 

 71             // In-place construction

 72             void construct( pointer p, const_reference c )

 73             {

 74                 new( reinterpret_cast<void*>(p) ) T(c);

 75             }

 76 

 77             // In-place destruction

 78             void destroy( pointer p )

 79             {

 80                 (p)->~T();

 81             }

 82 

 83             // Rebind to allocators of other types

 84             template <typename U>

 85             struct rebind

 86             {

 87                 typedef stack_alloc<U> other;

 88             };

 89 

 90             // Allocate raw memory

 91             pointer allocate( size_type n, const void* = NULL )

 92             {

 93                 void* praw = m_cur;

 94                 m_cur += n*sizeof(T);

 95                 if(m_cur+1>m_begin+m_max_bytes)

 96                 {

 97                     throw std::bad_alloc();

 98                 }

 99 

100                 return pointer(praw);

101             }

102 

103             void deallocate( void* p, size_type n)

104             {                

105             }

106 

107             // Non-standard Dinkumware hack for Visual C++ 6.0 compiler.

108             // VC 6 doesn't support template rebind.

109             char* _charalloc( size_type n )

110             {

111                 return reinterpret_cast<char*>( allocate( n, NULL ) );

112             }

113 

114             unsigned char* get_stack() const

115             {

116                 return m_begin;

117             }

118 

119             uchar*&            m_cur;

120             size_t            m_max_bytes;

121             uchar*            m_begin;

122         };

123 

124         template <typename T>

125         bool operator==( const stack_alloc<T>& lhs, const stack_alloc<T>& rhs) throw()

126         {

127             return lhs.get_stack() == rhs.get_stack();

128         }

129 

130         template <typename T>

131         bool operator!=( const stack_alloc<T>& lhs, const stack_alloc<T>& rhs) throw()

132         {

133             return lhs.get_stack() != rhs.get_stack();

134         }
View Code

 

你可能感兴趣的:(stack)