關於STL中stack的實現的討論

摘要: 文章討論了爲什麼大多數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; 
   } 
};
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章