关于STL中stack的实现的讨论

C++ STL标准模板库的容器使用大纲 一、std::array(封装固定大小数组的容器) 二、std::deque (双端队列) 三、std::forw 四、std::list 五、std::map 六、std::multimap 七、std::queue 八、std::stack 九、std::vector 十、std::set 阅读详情

摘要: 文章讨论了为什么大多数STL的stack的实现中,对于内部的容器默认选择deque容器;并且给出了自己的几个不同想法实现的stack;并进行了简单的性能比较测试;(文章最后给出了一个性能、特性都无懈可击的stack的实现!)

  文章来源于abp论坛中的一篇讨论帖子: http://bbs.allaboutprogram.com/viewtopic.php?t=1026这是自己开始接触泛型和STL时形成的一篇讨论;文章中借用了Elminster,papercrane,Innocentius,PolyRandom等人的部分观点

1:为什么大多数STL的stack的实现中,对于内部的容器默认选择deque容器?而不是vector? 
    STL中,stack对内部使用容器的函数调用主要有:push_back,back,pop_back等,也就是顺序容器都满足要求(包括vector,deque,list)。很多人应该和我一样,在STL之前看到的stack实现都是以动态数组来(甚至静态数组)实现为主,也就是接近于使用vector方案;那为什么STL偏偏选择deque呢!?
    我的分析:
    a.用vector实现中(push_back动作为分期摊还常数时间),如果发生容器的大小改变时,将可能产生一个大动作(申请空间,拷贝构造,释放原来的元素和空间,该动作成线性复杂度) 而且vector的很多实现版本中,容器在任何情况下都从不缩减已经申请的空间容量(swap技巧除外);
    b.用deque实现时,容器的大小改变时(数据量较大),动作比vector就小多了(常数复杂度),并且当容器的大小变小时,还可以适当减小容量;但push_back 的逻辑相对vector复杂一点;
    c.用list实现时,不用考虑空间容量变化;但每次的压入弹出开销(内存时间)较大,但很平稳;那么,经过分析,在不同的应用场合,为stack选择不同的内部容器是很有必要的;如果对stack有性能上的要求,就应该考虑这一点(甚至重新写一个最适应问题要求的stack); 比如:要求有最快的平均访问速度,而且大概的容量要求也清楚(比较衡定),那么,使用vector是个不错的选择 要求每次的访问时间平稳,而不在乎平均访问时间时,那么,可以考虑使用list;所以,库默认的deque是个不错的选择,它介于vector和list之间,并且很好的综合了两者的优势;另papercrane:“oncrete policy deque相对于stack来说就像傻瓜机,乱用也不会有什么太大的问题。如你所说的平均时间和最差时间的要求,我觉得就好像hash map和tree map的性能差别一样。 ”

    (提示:文章后面还有两种想进一步融合这三种方式各自优势的stack的实现,特别是最后那个实现也许推翻了这里的表面上得到的看法);


2.自己也来写一个stack;
  由于看到VC6中实现的太差,所以自己简单写了一个stack模版实现,性质比较接近于stack<T,vector<T> >, 代码如下:

template<class T>  
class
 mystack 
//
测试用 
//
没有考虑异常时的rollback语义 
//另: Elminster指出“使用 count 不是一个好主意。保存一个指向“下一个位置”的指针应该会效率更高,而且也更易读”;

{ 
public
: 
    typedef T               value_type; 
    typedef unsigned 
int
      size_type; 
    mystack():_lenght(
0),_count(0
) {} 
    
~
mystack() 
   { 
      
if (_lenght!=0
) 
      { 
         
for (int i=0;i<_count;++
i) 
         { 
            ((T
*)(&_vData[i*sizeof(T)]))->T::~
T(); 
         } 
      }
   } 
    
bool empty() const
    
      { 
return (0==
_count); } 
    size_type size() 
const
 
      { 
return
 _count; } 
    value_type
&
 top() 
      { 
return *(T*)(&_vData[(_count-1)*sizeof
(T)]); } 
    
const value_type& top() const
 
      { 
return *(T*)(&_vData[(_count-1)*sizeof
(T)]); } 
    
void push(const value_type&
 x) 
   { 
      
if (_count>=
_lenght) 
      { 
         _resize(); 
      } 
      
new ((T*)(&_vData[_count*sizeof
(T)])) T(x) ; 
      
++
_count; 
   } 
    
void
 pop() 
   { 
      
--
_count; 
      ((T
*)(&_vData[_count*sizeof(T)]))->T::~
T(); 
   } 
protected
: 
   std::vector
<unsigned char>
   _vData; 
   size_type      _lenght; 
   size_type      _count; 
   
void
 _resize() 
   { 
      
if (0==
_lenght) 
      { 
         _lenght
=32
; 
         _vData.resize(_lenght
*sizeof
(T)); 
      } 
      
else
 
      { 
         _lenght
*=2
; 
         std::vector
<unsigned char>
 new_vData; 
         new_vData.resize(
sizeof(T)*
_lenght); 
         
for (int i=0;i<_count;++
i) 
         { 
            T
& x=*(T*)(_vData.begin()+i*sizeof
(T)); 
            
new ((T*)(new_vData.begin()+i*sizeof
(T))) T(x); 
            (
&x)->T::~
T(); 
         } 
         _vData.swap(new_vData); 
      } 
   } 
}; 


测试环境:VC6,赛扬1G,256M内存
测试代码:(不好意思,代码风格被VC的环境影响太久,想改变这种风格ing)
另: Elminster指出测试代码里面,TestStack("stack_deque_T0 : ",stack_deque_T0()) 这个做法也不太妥当。TestStack 的第二个参数是 stackT&,把一个临时对象绑在非常量引用上可能会带来问题。

 typedef  int  Test0_T; // 简单 POD 类型 

class  Test1_T    // 较复杂的类 
{ 
public : 
   
char  _c; 
   
double  _d; 
   
int   _i; 
   
char *  _p; 
   Test1_T():_c(),_d(),_i(),_p(
new   char [ 20 ]) 
      {} 
   Test1_T(
const  Test1_T &  x) 
      :_c(),_d(),_i(),_p(
new   char [ 20 ]) 
      { (
* this ). operator   = (x);  } 
   
~ Test1_T(){ delete[] _p; } 
   Test1_T
&   operator   = ( const  Test1_T &  x) 
   { 
      _c
= x._c; _d = x._d; _i = x._i; 
      
for  ( int  i = 0 ;i < 20 ; ++ i) 
         _p[i]
= x._p[i]; 
      
return   * this ; 
   } 
}; 

__declspec( naked ) __int64 CPUCycleCounter()
// 获取当前CPU周期计数(CPU周期数)
{
 __asm
 {
  RDTSC    
// 0F 31   // eax,edx
  ret
 }
}

template
< class  stackT >  
int  testProc(stackT &  s, int  Count) 
{ 

   typename stackT::value_type vl
= stackT::value_type(); 
   typename stackT::value_type vx; 

   __int64   t0
= ::CPUCycleCounter(); // 返回CPU启动以来运行的周期数 

   
for  ( int  c = 0 ;c < 100 ; ++ c) 
   { 
      
int  i; 
      
for  (i = 0 ;i < Count; ++ i) 
         s.push(vl); 
      
for  (i = 0 ;i < Count; ++ i) 
         vx
= s.top(); 
      
for  (i = 0 ;i < Count; ++ i) 
         s.pop(); 
   } 

   __int64   t1
= ::CPUCycleCounter(); 

   
return    int ((t1 - t0) / 100 ); 
} 

template
< class  stackT >  
CString TestStack(PCSTR lab,stackT
&  s) 
{ 
   
int    t0 = testProc(s, 10 ); 
   
int    t1 = testProc(s, 1000 ); 
   
int    t2 = testProc(s, 100000 ); 

   CString str; 
   str.Format(
" %10d,%10d,%10d " ,t0,t1,t2); 
   str
+= char ( 13 );str += char ( 10 ); 
   
return  lab + str; 
} 


void  CSTACKTESTDlg::OnBUTTONTest() 
{ 
   
//  TODO: Add your control notification handler code here 
    using   namespace  std; 
   typedef stack
< Test0_T,deque < Test0_T >   >    stack_deque_T0; 
   typedef stack
< Test1_T,deque < Test1_T >   >    stack_deque_T1; 
   typedef stack
< Test0_T,vector < Test0_T >   >    stack_vector_T0; 
   typedef stack
< Test1_T,vector < Test1_T >   >    stack_vector_T1; 
   typedef stack
< Test0_T,list < Test0_T >   >    stack_list_T0; 
   typedef stack
< Test1_T,list < Test1_T >   >    stack_list_T1; 
   typedef mystack
< Test0_T >             mystack_T0; 
   typedef mystack
< Test1_T >             mystack_T1; 

   CString str; 
   str
+= TestStack( " stack_deque_T0 :  " ,stack_deque_T0()); 
   str
+= TestStack( " stack_vector_T0:  " ,stack_vector_T0()); 
   str
+= TestStack( " stack_list_T0  :  " ,stack_list_T0()); 
   str
+= TestStack( " mystack_T0     :  " ,mystack_T0()); 
   str
+= char ( 13 );str += char ( 10 ); 
   str
+= TestStack( " stack_deque_T1 :  " ,stack_deque_T1()); 
   str
+= TestStack( " stack_vector_T1:  " ,stack_vector_T1()); 
   str
+= TestStack( " stack_list_T1  :  " ,stack_list_T1()); 
   str
+= TestStack( " mystack_T1     :  " ,mystack_T1()); 

   
this -> m_str = str; // ::MessageBox(0,str,"",0); 
   UpdateData(FALSE); 
}

测试结果:(不同使用环境下的测试情况可能不同,该数据仅作参考)
(另:测试中使用的STL是VC6自带的)

(计时单位:万CPU周期)
N: 10 1000 100000
stack_deque_T0  : 2695,  65621,  12191532
stack_vector_T0 :  579,  47750,   9169942
stack_list_T0   : 6028, 957515, 167660924
mystack_T0      :  230,  13766,   2550403 (效果很好嘛)

stack_deque_T1 : 10699, 1168983, 270182336
stack_vector_T1:  8043, 1247187, 250648378
stack_list_T1  : 13043, 1988924, 424801657
mystack_T1     :  6796, 1240879, 252690706(对于复杂对象,与stack_vector_T1的差不多)

Elminster给出了他的测试结果:
(计时单位:tick count) :
stack_deque_T0  : 0 156 12969
stack_vector_T0 : 0  63  7562
mystack_T0      : 0  47  6766

stack_deque_T1  : 16 500 57765
stack_vector_T1 :  0 813 91843
mystack_T1      :  0 734 85469

环境是 amd athlon 1600+, win2k sp4, 256M ddr, vs.net 2003,最大速度优化。

Elminster:“结论比较有趣。对于拷贝动作比较轻量级的 T0,你的方案比 deque 和 vector 都快,但与 vector 相差不大。此时 deque 的性能落的比较后面,原因应该是 deque 的 push_back 的逻辑相对复杂(我看了看)。对于拷贝动作比较重的 T1 ,你的方案和 vector 反而要比 deque 慢。这里的原因应该是 resize 的时候拷贝的开销太重。其实我认为对于 stack 的行为模式,类似 deque 的存储结构会比较好,因为空间完全不需要连续,像vector 那样需要拷贝的 resize 是毫无必要的。你自己实现一个简洁的 deque style 数据结构,相信可以把stack 的性能再提升一个台阶。”

/////////////////////////////////////////
对上面自己写的stack做了些改进:
测试环境:VC6,XP,赛扬1G,256M内存
测试结果:

 template < class  T >  
class  mystack 
{ 
public : 
   
enum { type_sizes = sizeof (T) }; 
    typedef T               value_type; 
    typedef unsigned 
int       size_type; 
    mystack():_begin(
0 ),_end( 0 ),_last( 0 ) {} 
    
~ mystack() 
   { 
      
if  (size() > 0 ) 
      { 
         
for  (T *  i = _begin;i < _end; ++ i) 
            i
-> T:: ~ T(); 
      } 
      
if  (_begin != 0 ) delete[] (unsigned  char * )_begin;
   } 
    
bool  empty()  const     
      { 
return  ( 0 == size()); } 
    size_type size() 
const  
      { 
return  (_end - _begin); } 
    value_type
&  top() 
      { 
return   * (_end - 1 ); } 
    
const  value_type &  top()  const  
      { 
return   * (_end - 1 ); } 
    
void  push( const  value_type &  x) 
   { 
      
if  (_end < _last) 
      { 
         
new  (_end) T(x) ; 
         
++ _end; 
      } 
      
else  
      { 
         _resize(); 
         
new  (_end) T(x); 
         
++ _end; 
      } 
   } 
    
void  pop() 
   { 
      
-- _end; 
      _end
-> T:: ~ T(); 
   } 
protected : 
   T
*             _begin; 
   T
*             _end; 
   T
*             _last; 
   
void  _resize() 
   { 
      
if  ( 0 == _begin) 
      { 
         
const  unsigned  int  lenght = 32 ; 
         _begin
= (T * )(  new  unsigned  char [lenght * type_sizes]); 
         _end  
= _begin; 
         _last 
= _begin + lenght; 
      } 
      
else  
      { 
         unsigned 
int  old_size = size(); 
         unsigned 
int  lenght = (old_size << 1 ); 
         T
*  _pNewData = (T * )(  new  unsigned  char [lenght * type_sizes]); 
 

         
int  i = 0 ; 
         
try  
         { 
            
for  (;i < ( int )old_size; ++ i) 
               
new  (_pNewData  +  i) T( * (_begin + i)); 
         } 
         
catch (...) // rollback语义 
         { 
            
for  ( int  r = 0 ;r < i; ++ r) 
               (_pNewData 
+  r) -> T:: ~ T(); 
            delete[] (unsigned 
char * )_pNewData; 
            
throw ; 
         } 

         
for  ( int  j = 0 ;j < ( int )old_size; ++ j) 
            (_begin
+ j) -> T:: ~ T(); 
         T
*  old_begin = _begin; 
         _begin
= _pNewData; 
         _end  
= _begin + old_size; 
         _last 
= _begin + lenght; 
         delete[] (unsigned 
char * )old_begin; 
      } 
   } 
};

stack_deque_T0  : 2704,   65916,  11726143
stack_vector_T0 :  581,   45713,   9192746
stack_list_T0   : 5941, 1010118, 181184020
mystack_T0      :  172,   12210,   2590757

stack_deque_T1  : 10587, 1962722, 255087740
stack_vector_T1 :  8145, 1237245, 243123956
stack_list_T1   : 13128, 2005212, 398256062
mystack_T1      :  7127, 1173165, 251515265

测试环境:VS.net,XP,赛扬1G,256M内存
stack_deque_T0  :  525,   33407,   6468531
stack_vector_T0 :  574,   34710,   5236028
stack_list_T0   : 4753, 1060449, 165353094
mystack_T0      :  160,   10200,   2290310

stack_deque_T1  :  7470,  963957, 266757848
stack_vector_T1 :  8412, 1275307, 278246615
stack_list_T1   : 12001, 1993198, 475480319
mystack_T1      :  7317, 1209379, 262822216

对比VC6, vs.net的 deque的性能好像提高了不少,但和vector一样还有优化的空间

3.利用x86的虚拟空间地址原理来实现stack
    deque的实现中一般内部维护一个动态指针数组,这些指针指向数据块(每块保存多个元素),这些块在内存中是不连续的,然而deque利用软件的方式提供了一个对外的线性访问的假象;
    看一下现在的x86CPU,也有一个机制和这很像,即:保护的虚拟地址内存模型; 它把不连续的物理内存映射为一维线性内存模型,由于是硬件支持的映射,所以这种机制不会损失任何性能(deque、stack、vector等的实现也可以利用这一点)
    实现stack时,可以先开辟很大一块内存空间(足够),但不一次全部提交,开始只提交很少 的一部分物理页面,当需要增大容器容量时,只需要再提交部分物理页面给它,当需要减少容器容量时 可以收回部分页面;这个方案很像是deque的实现,但它可以获得vector式的线性内存访问能力和性能;
    当然这会占用较大量的虚拟内存地址空间,这个方案也可能和具体平台相关,但不会浪费真正的物理内存空间(如果是64位CPU,那么虚拟内存地址空间的浪费就可以不用考虑了:))


新的容器ExStack,它同时具有vector的线性访问能力,和deque的内存管理方式,所以我期待它具有这两者的性质: 

class  TAlloc // 移植时 在不同的平台下需要改变的部分 
{ 
public : 
   
static   void *  reserve(unsigned  int  size)          // 申请保留虚拟空间地址 
   { 
      
return  ::VirtualAlloc( 0 ,size,MEM_RESERVE,PAGE_READWRITE); 
   } 
   
static   bool  commit( void *  pbase,unsigned  int  offset,unsigned  int  size)    // 提交物理空间 
   { 
      
return   0 != ::VirtualAlloc(((BYTE * )pbase) + offset,size,MEM_COMMIT,PAGE_READWRITE); 
   } 
   
static   bool  free( void *  pbase)  // 解除提交的物理空间,并释放申请的虚拟空间地址 
   { 
      
return   0 != ::VirtualFree(pbase, 0 ,MEM_RELEASE); 
   } 
}; 

 

template
< class  T >  
class  ExStack 
{ 
public : 
   
enum { type_sizes = sizeof (T) };
    typedef T               value_type; 
    typedef unsigned 
int       size_type; 
    ExStack():_begin(
0 ),_end( 0 ),_last( 0 ) {} 
    
~ ExStack() 
   { 
      
if  (size() > 0 ) 
      { 
         
for  (T *  i = _begin;i < _end; ++ i) 
            i
-> T:: ~ T(); 
      } 
      
if  (_begin != 0 ) TAlloc::free(_begin); 
   } 
    
bool  empty()  const     
      { 
return  ( 0 == size()); } 
    size_type size() 
const  
      { 
return  (_end - _begin); } 
    value_type
&  top() 
      { 
return   * (_end - 1 ); } 
    
const  value_type &  top()  const  
      { 
return   * (_end - 1 ); } 
    
void  push( const  value_type &  x) 
   { 
      
if  (_end < _last) 
      { 
         
// std::_Construct(_end,x) ; 
          new  (_end) T(x) ; 
         
++ _end; 
      } 
      
else  
      { 
         _resize(); 
         
// std::_Construct(_end,x); 
          new  (_end) T(x) ; 
         
++ _end; 
      } 
   } 
    
void  pop() 
   { 
      
// 没有实现收回物理内存的语义
       -- _end; 
      _end
-> T:: ~ T(); 
   } 
protected : 
   T
*             _begin; 
   T
*             _end; 
   T
*             _last; 
   
void  _resize() 
   { 
      
if  ( 0 == _begin) 
      { 
         
const  unsigned  int  lenght = 4 * 1024 ; // 4KB 边界对齐 
         _begin = (T * )TAlloc::reserve( 256 * 1024 * 1024 ); // 预留地址空间 256 MB 
         TAlloc::commit(( void * )_begin, 0 ,lenght * type_sizes); 
         _end  
= _begin; 
         _last 
= _begin + lenght; 
      } 
      
else  
      { 
         unsigned 
int  old_size = size(); 
         unsigned 
int  lenght = (old_size << 1 ); 
         TAlloc::commit(_begin,old_size
* type_sizes,(lenght - old_size) 

* type_sizes); 
         _last 
= _begin + lenght; 
      } 
   } 
};
//测试条件更加接近于一般使用环境, 测试也更合理
int  testProc(stackT &  s, int  Count) // 注意测试条件变了很多!!! 
{ 
 

   typename stackT::value_type vl
= stackT::value_type(); 
   typename stackT::value_type vx; 

   __int64   t0
= ::CPUCycleCounter(); // 返回CPU启动以来运行的周期数 

   
for  ( int  c = 0 ;c < 100 ; ++ c) 
   { 
      stackT temps;
//     
       int  i; 
      
for  (i = 0 ;i < Count + 1 ; ++ i) // +1 
         temps.push(vl); 
      
for  (i = 0 ;i < Count; ++ i) 
         vx
= temps.top(); 
      
for  (i = 0 ;i < Count - 1 ; ++ i) // -1 
         temps.pop(); 
      
for  (i = 0 ;i < (Count >> 1 ); ++ i) // ! 
         temps.push(vl); 
   } 

   __int64   t1
= ::CPUCycleCounter(); 

   
return    int ((t1 - t0) / 100 ); 
}
测试环境:XPvc.net赛扬1GHz256MB

测试结果:

            N  =         10       1000     100000
stack_deque_T0 :       2942,    205538,  54123660
stack_vector_T0:       4903,     66692,  21136702
stack_list_T0  :       8003,   1232967, 239859174
mystack_T0     :       1120,     20431,  12025250
ExStack_T0     :      40710,     75998,   5908666

stack_deque_T1 :      18444,   2383833, 490387023
stack_vector_T1:      35764,   3281555,1130011712
stack_list_T1  :      19624,   2842182, 588527582
mystack_T1     :      11764,   2285440, 749906486
ExStack_T1     :      56646,   1680889, 372481178

mystack: 在大多数情况下都很优秀,但当数据量较大并且数据类型较复杂时,性能迅速下降std::stack<T,vector<T> >: 性质与mystack一致,但VC6版实现得太差了;(这种库看了就让人生气)std::stack<T,list<T> >: 绝对速度(平均速度)没法和其他实现比,但他的优点不在这;ExStack: 初始化和销毁开销太大了些(否则ExStack在很多测试中都将领先),数据量较大时,不出意料的在简单和复杂数据类型中都领先于对手(这时才显示出综合了vector与deque的优势);但是ExStack的适用范围实在太小了,比我预期的适用范围差;

(PolyRandom:我觉得这个ExStack不错,而且可以用的地方应该不少。)


4. 极速stack的诞生myfast_stack

    一次尝试(前奏):   还是忍不住自己写了一个deque内存管理方式的_myfast_stack;性质接近于std::static<T,deque<T> >; 其实比我想象中简单多了,很快就实现出来了(因为不需要实现一个完整的deque),测试时各项性能也很优秀;只是在“简单数据类型、元素个数N很小”时才输给了vector内存管理方式实现的mystack! 这一点好像在意料之中。

    是否这就是极限了呢?我准备把源码和测试结果发布出来的时候,却突然有了新的想法... 

一般deque内存管理需要用一个动态数组来保存指向数据块的指针,因为deque要求随机访问能力;但stack访问时明显没有随机访问特性的要求,所以 保存这些指针的数据结构最低需求也是满足stack接口就足够了;进一步的改进方案出来了,先用一个list来管理这些数据块,再把list的自己的数据成员与需要管理的数据空间合成放在一起(放在同一个数据块上);


哈哈,综合性能新的明星myfast_stack诞生了; 让人不敢相信的测试结果!!!


测试环境:Win2000,VC6,赛杨466,128M ( 自己的老古董电脑 )

             N =         20       1000      50000
stack_deque_T0 :        434,      9355,    494481
stack_vector_T0:        509,     11770,    583668
stack_list_T0  :       1330,     82371,   7071753
mystack_T0     :        103,      3248,    323565
ExStack_T0     :       3258,      5580,    163168
myfast_stack_T0:         97,      2682,    190075

stack_deque_T1 :       2464,    135898,  11981644
stack_vector_T1:       4042,    228555,  22766841
stack_list_T1  :       3281,    207232,  16723651
mystack_T1     :       1916,    206413,  21782916
ExStack_T1     :       5228,    129881,  10933717
myfast_stack_T1:       1983,    124296,  10399781


实现的实质还是deque方式的,不管从那方面来看我认为它都可以将其他几个vector和list等的实现淘汰掉!

!!!myfast_stack太恐怖了,几乎没有缺陷!!!

///////myfast_stack源代码//////////
// 管理内存的list 
template < unsigned  int  byte_size >  
class  Tdata_list // 管理myfast_stack的内存 
{ 
   
struct  TNode // 节点类型 
   { 
      TNode
*          pPrev; 
      TNode
*          pNext; 
      unsigned 
char    Data[byte_size]; // 数据空间 
   }; 
public : 
   Tdata_list():pNodeBegin(
0 ),pNodeCur( 0 ),_size( 0 ){} 
   unsigned 
int  size()  const  {  return  _size; } 
   
void  push() // 配置空间 
   { 
      
if  ( 0 == pNodeBegin) 
      { 
         pNodeBegin
= new  TNode; 
         pNodeBegin
-> pPrev = 0 ; 
         pNodeBegin
-> pNext = 0 ; 
         pNodeCur
= pNodeBegin; 
      } 
      
else   if  ( 0 != pNodeCur -> pNext) // 还有一个空余的Node 
      { 
         pNodeCur
= pNodeCur -> pNext; 
      } 
      
else  
      { 
         TNode
*  pNodeEnd = new  TNode; 
         pNodeEnd
-> pPrev = pNodeCur; 
         pNodeEnd
-> pNext = 0 ; 
         pNodeCur
-> pNext = pNodeEnd; 
         pNodeCur
= pNodeEnd;    
      } 
      
++ _size; 
   } 
   
~ Tdata_list() 
   { 
      
for  (TNode *  i = pNodeBegin;i != 0 ; ) 
      { 
         TNode
*  pNext = i -> pNext; 
         delete i; 
         i
= pNext; 
      } 
   } 
   unsigned 
char *  top() 
   { 
         
return  pNodeCur -> Data; 
   } 
   
void  pop() 
   { 
      TNode
*&  pTmp = pNodeCur -> pNext; 
      
if  (pTmp != 0 ) 
      { 
         delete pTmp;
// 留一个空余Node,多余的释放 
         pTmp = 0 ; 
      } 
      pNodeCur
= pNodeCur -> pPrev; 
      
-- _size; 
   } 
private : 
   TNode
*    pNodeCur; 
   TNode
*    pNodeBegin; 
   unsigned 
int  _size; // 用来追踪list的使用大小 
}; 


// myfast_stack 
// 注意它的实现并没有以降低stack的通用能力来提高性能 
// 改进可能:1.提供专署的内存分配器,而不是默认的new/delete; (就可以和SGI中的stack对比测试了)
template < class  T, bool  IsPOD = false >  
class  myfast_stack 
{ 
public : 
   
enum { type_sizes = sizeof (T), // ! 
         node_width = ( 1020 / type_sizes) + 1 // 使用这种策略,stack<T,list<T> >也没有存在必要了 
      }; 
    typedef T                           value_type; 
    typedef unsigned 
int                   size_type; 
   typedef Tdata_list
< type_sizes * node_width >    Tbase_alloc; 

    myfast_stack():_node_begin(
0 ),_node_cur( 0 ),_node_last( 0 ) {} 
    
~ myfast_stack() 
   { 
      
if  (( ! IsPOD) && (_NodeList.size() > 0 )) 
      { 
         
for  (T *  i = _node_begin;i < _node_cur; ++ i) 
            i
-> T:: ~ T(); 
         
int  nsize = ( int )_NodeList.size(); 
         
for  ( int  j = 0 ;j < (nsize - 1 ); ++ j) 
         { 
            _NodeList.pop(); 
            _node_cur
= (T * )_NodeList.top(); 
            
for  ( int  i = 0 ;i < node_width; ++ i) 
            { 
               _node_cur
-> T:: ~ T(); 
               
++ _node_cur; 
            } 
         } 
      } 
   } 
    
bool  empty()  const     
      { 
return  ( 0 == size()); } 
    size_type size() 
const  
      { 
return  (_node_last - _node_begin) + node_width * (min(( int )_NodeList.size() - 1 , 0 )); } 
    value_type
&  top() 
      { 
return   * (_node_cur - 1 ); } 
    
const  value_type &  top()  const  
      { 
return   * (_node_cur - 1 ); } 
    
void  push( const  value_type &  x) 
   { 
      
if  (_node_cur == _node_last) 
      { 
         _ToNextNode(); 
      } 
      
// std::_Construct(_node_cur,x); 
       new  (_node_cur) T(x) ; 
      
++ _node_cur; 
   } 
    
void  pop() 
   { 
      
if  (_node_cur == _node_begin) 
      { 
         _ToPrevNode(); 
      } 
      
-- _node_cur; 
      _node_cur
-> T:: ~ T(); 
   } 
protected : 
   T
*             _node_begin; 
   T
*             _node_cur; 
   T
*             _node_last; 
   Tbase_alloc      _NodeList; 
   
void  _ToNextNode() 
   { 
      _NodeList.push();    
      _node_begin
= (T * )(_NodeList.top()); 
      _node_last
= _node_begin + node_width; 
      _node_cur
= _node_begin; 
   } 
   
void  _ToPrevNode() 
   { 
      _NodeList.pop(); 
      _node_begin
= (T * )(_NodeList.top()); 
      _node_last
= _node_begin + node_width; 
      _node_cur
= _node_last; 
   } 
};
 
Cortex-M FPU的Lazy Stacking机制 By: Ailson Jack Date: 2020.08.15 个人博客:http://www.only2fire.com/ 本文在我博客的地址是:http://www.only2fire.com/archives/121.html,排版更好,便于学习,也可以去我博客逛逛,兴许有你想要的内容呢。 1.浮点运算指令 浮点运算指令用于FPU单元的单精度浮点运算。浮点运算指令都是用V开头的汇编指令。 只有在FPU开启的状态下,才能运行这些指令。 如果在FPU没有开启的状态下,执行... 阅读详情

相关推荐

C++STL剖析(四)—— stack和queue的概念和使用

stack、queue的介绍和使用

Edison's 小宇宙 2万+

栈---->动态实现

动态栈 栈可以使用静态数组来模拟实现,但是使用静态数组时,栈的大小就必须是固定的,为了弥补这种局限性,栈可以使用动态存储空间,因为在使用过程中可移动台的调整大小; typedef struct{ int key; }element; element* stack; MALLOC(stack,sizeof(stack)); 动态存储空间的扩容问题,一般来说当栈满之后,直接执行的是加倍策略; v

bug_maker 532

用Charles抓包调试iOS应用?这份保姆级配置指南帮你绕过所有坑

本文提供了一份在Mac上使用Charles抓包调试iOS应用的保姆级配置指南。详细讲解了从Charles安装、Mac端代理设置、根证书安装,到iOS设备连接同一WiFi、配置代理以及最关键的系统证书信任等全流程步骤,并分享了SSL代理规则、断点调试等进阶技巧,帮助开发者高效定位和解决网络请求问题。

rice5的博客 640

C++/C++11中std::stack的使用

C++/C++11中std::stack的使用

网络资源是无限的 1万+

IPsec (主模式,野蛮模式)

IPsec vpn的主模式和野蛮模式

七汣的博客 3414

【软件介绍】GWAS meta分析软件:METAL

Meta-analysis是对多个GWAS分析结果进行综合评价。METAL是GWAS meta分析最常用的工具之一,本文简单介绍METAL软件基本用法。

hucy_Bioinfo 1万+

c++STL中stack,queue使用方法和模拟实现,及适配器模式编程

1.1 stack的介绍栈是计算机中一个重要的数据结构,因其先进先出的特性,可以解决许多结构上的问题,进行栈的学习,了解它的底层是很有必要的。1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。

qq_58761784的博客 1632

AI工具 | 每个打工人必备的15款人工智能超级工具

不过,还是有一些靠谱的人工智能平台,它们可不只是营销噱头。有些AI公司正试图改变游戏规则。人工智能工具让人们能够更聪明地工作,而不是更辛苦地劳作。如果你想知道未来有什么,那就来看看这10款人工智能工具吧,它们将彻底改变我们的工作方式。

Vampire_2017的博客 5万+

STL: 容器适配器stack 与 queue

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。

qq_72916130的博客 2289

RK+linux教程:RK3588 RGA硬件加速在YOLOv5图像预处理中的深度应用教程

摘要: 本教程深入解析了RK3588芯片的RGA硬件加速器在YOLOv5图像预处理中的应用。通过专用硬件设计,RGA支持图像缩放、边界填充等核心操作,结合零拷贝技术显著提升处理效率。重点分析了LetterBox算法的RGA实现,包括参数计算、边界填充和性能优化,并与传统CPU处理对比,展示了RGA在高分辨率图像处理中的优势。教程还提供了统一的预处理接口设计,便于在OpenCV与RGA方案间灵活切换,为RK3588平台的深度学习部署提供实用优化方案。

LXY_LOVE36的博客 975

锐明技术Crocus系统 DeviceState.do SQL注入漏洞复现

锐明技术Crocus系统 DeviceState.do 接口存在SQL注入漏洞,经过身份验证的远程攻击者除了可以利用SQL注入漏洞获取数据库中的信息(例如,管理员后台密码、站点的用户个人信息)之外,甚至在高权限的情况可向服务器中写入木马,进一步获取服务器系统权限。

0xSec 453

STL-stack的模拟实现

本文内容讲了stack 的模拟实现,更重要是我们初步接触了STL六大组件之一的适配器,知道了STL泛型的强大之处。

fengjunziya的博客 236

某勾网之登录过程逆向

拉勾网的登录过程逆向,包含请求头(X-S-HEADER、X-K-HEADER、X-SS-REQ-HEADER等) 以及 请求参数/返回数据的加解密。

shanf7921的博客 3万+

STL-stack和queue

stack- queue-priority_queue

qq_62223120的博客 1006

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

STL 容器 stack 的使用详解

stack容器 stack是堆栈容器,是一种“先进后出”的容器。 #include <stack> stack是基于deque容器而实现的容器 stack对象的默认构造 stack采用模板类实现, stack对象的默认构造形式: stack stkT; stack stkInt; //一个存放int的stack容器。 stack stkFloat; ...

weifc-wei 的博客 535

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

【C++ STL】容器适配器 Container adapter(stack & queue & priority_queue)

文章目录一、STL - stack1.1 stack 的介绍1.2 stack 的使用1.3 stack 的模拟实现二、STL - queue 的介绍2.1 queue 的介绍2.2 queue 的使用2.3 queue 的模拟实现三、STL - priority_queue3.1 priority_queue 的介绍3.2 priority_queue 的使用3.3 仿函数(⭐重要)① 什么是仿函数② 模板实例化时,仿函数的使用3.4 priority_queue 的模拟实现四、容器适配器4.1 什么是容

codewinter的博客 1541

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

【C++】适配器模式手搓STL的stack和queue

适配器模式手搓STL的stack和queue一篇文章就够了!!!

有梦想的电信狗 1361

STL源码剖析 stack 栈 概述->(使用deque双端队列 / list链表)作为stack的底层容器

Stack是一种先进后出的数据结构,他只有一个出口 stack允许 新增元素、移除元素、取得最顶端的元素,但是无法获得stack的内部数据,因此satck没有遍历行为 Stack定义的完整列表 (双端队列作为Stack的底层容器) 将deque作为Stack的底部结构,对其原有的接口进行适配,使其满足"先进后出"的特性 deque是双向开口的数据结构,只需要封闭deque的头端开口(缺省实现),便轻而易举的形成了一个stack。 Stack基于deque,这种“修改某物的接口 形成另外一种事物.

CHYabc123456hh的博客 945

10.STL中stack和queue的基本使用(附习题)

快一起来学习stack和queue吧

No Game No Life 2380

STL——stack和queue

仿函数就是使用起来类似于使用一个函数,但是它实际上不是调用函数,而是通过类的对象调用类中重载的括号运算符成员函数,从而达到我们的目的。仿函数可以作为模板参数使用,因为每个仿函数都拥有自己的类型。仿函数比一般函数更灵活。

weixin_50601177的博客 274

C++:stl:stack、queue、priority_queue介绍及模拟实现和容量适配器deque介绍

本文主要介绍c++中stl的栈、队列和优先级队列并对其模拟实现,对deque进行一定介绍并在栈和队列的模拟实现中使用。

Bottle2023的博客 468
上一篇: 深入研究 STL Deque 容器
下一篇: STL中的函数对象(Funciont Objects)
firectrl
博客等级 码龄20年 4粉丝 32原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值