﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-Just enjoy programming-随笔分类-c++ STL源码分析</title><link>http://www.cppblog.com/tankzhouqiang/category/16816.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 02 Jun 2011 10:44:54 GMT</lastBuildDate><pubDate>Thu, 02 Jun 2011 10:44:54 GMT</pubDate><ttl>60</ttl><item><title>c++ STL 容器</title><link>http://www.cppblog.com/tankzhouqiang/archive/2011/06/02/147939.html</link><dc:creator>周强</dc:creator><author>周强</author><pubDate>Thu, 02 Jun 2011 03:28:00 GMT</pubDate><guid>http://www.cppblog.com/tankzhouqiang/archive/2011/06/02/147939.html</guid><wfw:comment>http://www.cppblog.com/tankzhouqiang/comments/147939.html</wfw:comment><comments>http://www.cppblog.com/tankzhouqiang/archive/2011/06/02/147939.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tankzhouqiang/comments/commentRss/147939.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tankzhouqiang/services/trackbacks/147939.html</trackback:ping><description><![CDATA[参考：STL源码分析<br /><br />（一）vector容器<br />vector的数据安排以及操作方式，与array非常相似。两者的唯一区别在于空间的运用的灵活性。array是静态空间，一旦配置了就不能改变。vector是动态空间，随着元素的加入，它的内部机制会自行扩充空间以容纳新元素。因此，vector的运用对于内存的合理利用与运用的灵活性有很大的帮助，我们再也不必因为害怕空间不足而一开始要求一个大块的array。<br /><br />vector动态增加大小，并不是在原空间之后持续新空间（因为无法保证原空间之后尚有可供配置的空间），而是以原大小的两倍另外配置一块较大的空间，然后将原内容拷贝过来，然后才开始在原内容之后构造新元素，并释放原空间。因此，对vector的任何操作，一旦引起空间重新配置，指向原vector的所有迭代器就都失效了。<br /><br />（二）list容器<br />相对于vector的连续空间，list就显得复杂许多，它的好处是每次插入或删除一个元素，就配置或释放一个元素空间。因此，list对于空间的运用有绝对的精准，一点也不浪费。而且，对于任何位置的元素插入或元素移除，list永远是常数时间。STL中的list是一个双向链表，而且是一个环状双向链表。<br /><br />（三）deque容器<br />&nbsp;deque 是一种双向开口的连续线性空间。所谓双向开口，意思是可以在队尾两端分别做元素的插入和删除操作。deque和vector的最大差异，一在于deque允许于常数时间内对起头端进行元素的插入或移除操作，二在于deque没有所谓容量观念，因为它是动态地以分段连续空间组合而成，随时可以增加一段新的空间并链接在一起。换句话说，像vector那样"因旧空间不足而重新配置一块更大空间，然后复制元素，再释放旧空间"这样的事情在 deque是不会发生的。<br /><br />deque是由一段一段的定量连续空间构成。一旦有必要在deque的前端或尾端增加新空间，便配置一段定量连续空间，串接在整个deque的头端或尾端。deque的最大任务，便是在这些分段的定量连续空间上，维护其整体连续的假象，并提供随机存取的接口。避开了"重新配置，复制，释放"的轮回，代价则是复杂的迭代器架构。因为有分段连续线性空间，就必须有中央控制，而为了维持整体连续的假象，数据结构的设计及迭代器前进后退等操作都颇为繁琐。<br /><br />deque采用一块所谓的map作为主控。这里的map是一小块连续空间，其中每个元素都是指针，指向另一段连续线性空间，称为缓冲区。缓冲区才是deque的存储空间主体。SGI STL允许我们指定缓冲区大小，默认值0表示将使用512 bytes缓冲区。<br /><br />（四）stack<br />stack 是一种先进后出（First In Last Out , FILO）的数据结构。它只有一个出口，stack 允许新增元素，移除元素，取得最顶端元素。但除了最顶端外，没有任何其它方法可以存取stack的其它元素，stack不允许遍历行为。<br /><br />以某种容器作为底部结构，将其接口改变，使之符合&#8220;先进后出&#8221;的特性，形成一个stack，是很容易做到的。deque是双向开口的数据结构，若以deque为底部结构并封闭其头端开口，便轻而易举地形成了一个stack.因此，SGI STL 便以deque作为缺省情况下的stack底部结构，由于stack 系以底部容器完成其所有工作，而具有这种"修改某物接口，形成另一种风貌"之性质者，称为adapter（配接器），因此，STL stack 往往不被归类为container(容器)，而被归类为 container adapter.<br /><br />(五） queue<br />queue是一种先进先出(First In First Out,FIFO) 的数据结构。它有两个出口，queue允许新增元素，移除元素，从最底端加入元素，取得最顶端元素。但除了最底端可以加入，最顶端可以取出外，没有任何其它方法可以存取queue的其它元素。<br /><br /><div>以某种容器作为底部结构，将其接口改变，使之符合&#8220;先进先出&#8221;的特性，形成一个queue，是很容易做到的。deque是双向开口的数据结构，若以 deque为底部结构并封闭其底部的出口和前端的入口，便轻而易举地形成了一个queue.因此，SGI STL  便以deque作为缺省情况下的queue底部结构，由于queue  系以底部容器完成其所有工作，而具有这种"修改某物接口，形成另一种风貌"之性质者，称为adapter（配接器），因此，STL queue往往不被归类为container(容器)，而被归类为 container adapter.</div><br />(六)heap<br />heap并不归属于STL容器组件，它是个幕后英雄，扮演priority queue的助手。priority queue允许用户以任何次序将任何元素推入容器中，但取出时一定按从优先权最高的元素开始取。按照元素的排列方式，heap可分为max-heap和min-heap两种，前者每个节点的键值(key)都大于或等于其子节点键值，后者的每个节点键值(key)都小于或等于其子节点键值。因此， max-heap的最大值在根节点，并总是位于底层array或vector的起头处；min-heap的最小值在根节点，亦总是位于底层array或vector起头处。STL 供应的是max-heap，用c++实现。<br />堆排序c语言实现<div>http://www.cppblog.com/tankzhouqiang/archive/2011/03/21/142413.html</div><br />（七）priority_queue<br />priority_queue是一个拥有权值观念的queue,它允许加入新元素，移除旧元素，审视元素值等功能。由于这是一个queue，所以只允许在底端加入元素，并从顶端取出元素，除此之外别无其它存取元素的途径。priority_queue带有权值观念，其内的元素并非依照被推入的次序排列，而是自动依照元素的权值排列（通常权值以实值表示）。权值最高者，排在最前面。缺省情况下priority_queue系利用一个max-heap完成，后者是一个以vector表现的 complete binary tree.max-heap可以满足priority_queue所需要的"依权值高低自动递减排序"的特性。<br />priority_queue完全以底部容器作为根据，再加上heap处理规则，所以其实现非常简单。缺省情况下是以vector为底部容器。queue以底部容器完成其所有工作。具有这种"修改某物接口，形成另一种风貌"之性质者，称为adapter(配接器)，因此，STL priority_queue往往不被归类为container(容器)，而被归类为container adapter.<br /><br />(八)set,multiset<br />set的特性是，所有元素都会根据元素的键值自动被排序。set的元素不像map那样可以同时拥有实值(value)和键值(key), set 元素的键值就是实值，实值就是键值，set不允许两个元素有相同的值。set是通过红黑树来实现的，由于红黑树（RB-tree）是一种平衡二叉搜索树，自动排序的效果很不错，所以标准的STL的set即以RB-Tree为底层机制。又由于set所开放的各种操作接口，RB-tree也都提供了，所以几乎所有的set操作行为，都只有转调用RB-tree的操作行为而已。<br /><br />multiset的特性以及用法和set完全相同，唯一的差别在于它允许键值重复，因此它的插入操作采用的是底层机制RB-tree的insert_equal()而非insert_unique().<br /><br />（九）map,multimap<br />map的特性是，所有元素都会根据元素的键值自动被排序。map的所有元素都是pair,同时拥有实值（value）和键值（key）.&nbsp; pair的第一元素被视为键值，第二元素被视为实值。map不允许两个元素拥有相同的键值.由于RB-tree是一种平衡二叉搜索树，自动排序的效果很不错，所以标准的STL map即以RB-tree为底层机制。又由于map所开放的各种操作接口，RB-tree也都提供了，所以几乎所有的map操作行为，都只是转调RB-tree的操作行为。<br />multimap的特性以及用法与map完全相同，唯一的差别在于它允许键值重复，因此它的插入操作采用的是底层机制RB-tree的insert_equal()而非insert_unique。 <br /><img src ="http://www.cppblog.com/tankzhouqiang/aggbug/147939.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tankzhouqiang/" target="_blank">周强</a> 2011-06-02 11:28 <a href="http://www.cppblog.com/tankzhouqiang/archive/2011/06/02/147939.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>STL源码分析空间配置器</title><link>http://www.cppblog.com/tankzhouqiang/archive/2011/05/12/146238.html</link><dc:creator>周强</dc:creator><author>周强</author><pubDate>Thu, 12 May 2011 02:19:00 GMT</pubDate><guid>http://www.cppblog.com/tankzhouqiang/archive/2011/05/12/146238.html</guid><wfw:comment>http://www.cppblog.com/tankzhouqiang/comments/146238.html</wfw:comment><comments>http://www.cppblog.com/tankzhouqiang/archive/2011/05/12/146238.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/tankzhouqiang/comments/commentRss/146238.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tankzhouqiang/services/trackbacks/146238.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 看过STL空间配置器的源码，总结一下：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (1)STL空间配置器：主要分三个文件实现，stl_construct.h&nbsp; 这里定义了全局函数construct()和destroy(),负责对象的构造和析构。stl_alloc.h文件中定义了一，二两级配置器，彼此合作，配置器名为alloc. stl_uninitialized.h 这里定义了一些全局函数，用来填充(fill)或复制(copy)大块内存数据，他们也都隶属于STL标准规划。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在stl_alloc.h中定义了两级配置器，主要思想是申请大块内存池，小块内存直接从内存池中申请，当不够用时再申请新的内存池，还有就是大块内存直接申请。当申请空间大于128字节时调用第一级配置器，第一级配置器没有用operator::new和operator::delete来申请空间，而是直接调用malloc/free和realloc，并且实现了类似c++中new-handler的机制。所谓c++ new handler机制是，你可以要求系统在内存配置需求无法被满足时，调用一个指定的函数。换句话说，一旦::operator::new无法完成任务，在丢出std::bad_alloc异常状态之前，会先调用由客端指定的处理例程，该处理例程通常称为new-handler.new-handler解决内存做法有特定的模式。SGI第一级配置器的allocate()和realloc都是在调用malloc和realloc不成功后，改调用oom_malloc()和oom_realloc().后两者都有内循环，不断调用"内存不足处理例程",期望在某次调用之后，获得足够的内存而圆满完成任务。但如果&#8220;内存不足处理例程&#8220;并未被客端设定，oom_malloc()和oom_realloc便调用_THROW_BAD_ALLOC, 丢出bad_alloc异常信息，或利用exit(1)硬生生中止程序。<br>&nbsp;&nbsp;&nbsp;&nbsp; 在stl_alloc.h中定义的第二级配置器中，如果区块够大，超过128字节时，就移交第一级配置器处理，当区块小于128字节时，则以内存池管理，此法又称为次层配置，每次配置一大块内存，并维护对应的自由链表(free-list).下次若再有相同大小的内存需求，就直接从free-list中拔出。如果客端释还小额区块，就由配置器回收到free-lists中，配置器除了负责配置，也负责回收。为了管理方便，SGI第二级配置器会主动将任何小额区块的内存需求量上调至8的倍数。并维护16个free-lists,各自管理大小分别为8，16，24，32，40，48，56，64，72，80，88，96，104， 112，120，128 字节的小额区块。当申请小于等于128字节时就会检查对应的free list，如果free-list中有可用的区块，就直接拿来，如果没有，就准备为对应的free-list 重新填充空间。新的空间将取自内存池，缺省取得20个新节点，如果内存池不足（还足以一个以上的节点），就返回的相应的节点数.如果当内存池中连一个节点大小都不够时，就申请新的内存池,大小为2*total_bytes+ROUND_UP(heap_size&gt;&gt;4), totoal_bytes 为申请的空间大小，ROUND_UP调整为8的倍数，heap_size为当前总申请内存池的大小。如果申请该内存池成功就把原来内存池中剩下的空间分配给适当的free-list.万一山穷水尽，整个system heap空间都不够了（以至无法为内存池注入源头活水），malloc()行动失败，就会四处寻找有无"尚有未用区块，且区块足够大 "之free lists.找到了就挖一块交出，找不到就调用第一级配置器。第一级配置器其实也是使用malloc来配置内存。但它有out-of-memory处理机制（类似new-handler机制），或许有机会释放其他的内存拿来此处使用。如果可以就成功，否则发出bad_alloc异常。<br><br>参考：STL源码分析<br><br> <img src ="http://www.cppblog.com/tankzhouqiang/aggbug/146238.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tankzhouqiang/" target="_blank">周强</a> 2011-05-12 10:19 <a href="http://www.cppblog.com/tankzhouqiang/archive/2011/05/12/146238.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个简单的空间配置器</title><link>http://www.cppblog.com/tankzhouqiang/archive/2011/05/04/145617.html</link><dc:creator>周强</dc:creator><author>周强</author><pubDate>Tue, 03 May 2011 16:55:00 GMT</pubDate><guid>http://www.cppblog.com/tankzhouqiang/archive/2011/05/04/145617.html</guid><wfw:comment>http://www.cppblog.com/tankzhouqiang/comments/145617.html</wfw:comment><comments>http://www.cppblog.com/tankzhouqiang/archive/2011/05/04/145617.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tankzhouqiang/comments/commentRss/145617.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tankzhouqiang/services/trackbacks/145617.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 以STL的运用角度而言，空间配置器时最不需要介绍的东西，它总是隐藏在一切组件的背后，整个STL的操作对象（所有的数值）都存放在容器之内，而容器一定需要配置空间以置放资料。下面是一个简单空间配置器代码（来自 STL源码剖析）：<br>//jjalloc.h<br>#ifndef _JJALLOC_<br>#define _JJALLOC_<br><br>#include&lt;new&gt;<br>#include&lt;cstddef&gt;<br>#include&lt;cstdlib&gt;<br>#include&lt;climits&gt;<br>#include&lt;iostream&gt;<br><br>using namespace std;<br><br>namespace JJ<br>{<br>template&lt;class T&gt;<br>&nbsp;&nbsp; &nbsp;inline T* _allocate(ptrdiff_t size,T*)<br>&nbsp;&nbsp; &nbsp;{<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;set_new_handler(0);<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;T *tmp=(T*)(::operator new((size_t)(size* sizeof(T))));<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if(tmp==0){<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;cerr&lt;&lt;"out of memory"&lt;&lt;endl;<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;exit(1);<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return tmp;<br>&nbsp;&nbsp; &nbsp;}<br><br>template&lt;class T&gt;<br>&nbsp;&nbsp; &nbsp;inline void _deallocate(T* buffer)<br>&nbsp;&nbsp; &nbsp;{<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;::operator delete(buffer);<br>&nbsp;&nbsp; &nbsp;}<br><br>template&lt;class T1,class T2&gt;<br>&nbsp;&nbsp; &nbsp;inline void _construct(T1 *p,const T2&amp; value)<br>&nbsp;&nbsp; &nbsp;{<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;new(p)T1(value);//placement new operator<br>&nbsp;&nbsp; &nbsp;}<br><br>template&lt;class T&gt;<br>&nbsp;&nbsp; &nbsp;inline void _destroy(T* ptr)<br>&nbsp;&nbsp; &nbsp;{<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ptr-&gt;~T();<br>&nbsp;&nbsp; &nbsp;}<br><br>template&lt;class T&gt;class allocator{<br>&nbsp;&nbsp; &nbsp;public:<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;typedef&nbsp;&nbsp; &nbsp;T&nbsp;&nbsp; &nbsp;value_type;<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;typedef T*&nbsp;&nbsp; &nbsp;pointer;<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;typedef const T*&nbsp;&nbsp; &nbsp;const_pointer;<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;typedef T&amp;&nbsp;&nbsp; &nbsp;reference;<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;typedef const T&amp;&nbsp;&nbsp; &nbsp;const_reference;<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;typedef size_t&nbsp;&nbsp; &nbsp;size_type;<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;typedef ptrdiff_t&nbsp;&nbsp; &nbsp;difference_type;<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;template&lt;class U&gt;<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;struct rebind<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;typedef allocator&lt;U&gt;other;<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;};<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;pointer allocate(size_type n,const void * hint=0)<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return _allocate((difference_type)n,(pointer)0);<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;void deallocate(pointer p,size_type n)<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;_deallocate(p);<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;void construct(pointer p,const T&amp; value)<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;_construct(p,value);<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;void destroy(pointer p){_destroy(p);}<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;pointer address(reference x){return (pointer)&amp;x;}<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;const_pointer const_address(const_reference x)<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return (const_pointer)&amp;x;<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;size_type max_size()const{<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return size_type(UINT_MAX/sizeof(T));<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br>};<br>}//end of namespace JJ<br><br>#endif<br><br><br>//jjalloc.cc,测试上面这个简单的配置器<br>#include"jjalloc.h"<br>#include&lt;vector&gt;<br>#include&lt;iostream&gt;<br><br>using namespace std;<br><br>int main()<br>{<br>&nbsp;&nbsp; &nbsp;int ia[5]={0,1,2,3,4};<br>&nbsp;&nbsp; &nbsp;unsigned int i;<br><br>&nbsp;&nbsp; &nbsp;vector&lt;int,JJ::allocator&lt;int&gt; &gt;iv(ia,ia+5);<br><br>&nbsp;&nbsp; &nbsp;for(i=0;i&lt;iv.size();i++)<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;cout&lt;&lt;iv[i]&lt;&lt;' ';<br>&nbsp;&nbsp; &nbsp;cout&lt;&lt;endl;<br>}
<br><br>
<br><br><img src ="http://www.cppblog.com/tankzhouqiang/aggbug/145617.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tankzhouqiang/" target="_blank">周强</a> 2011-05-04 00:55 <a href="http://www.cppblog.com/tankzhouqiang/archive/2011/05/04/145617.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>