??xml version="1.0" encoding="utf-8" standalone="yes"?>日韩人妻无码精品久久免费一,久久国产成人午夜AV影院,婷婷久久久亚洲欧洲日产国码AV
http://www.shnenglu.com/lwch/category/19336.html【QQ:510134884】【Email:<a href="mailto:lwch748@gmail.com">lwch748@gmail.com</a>?/description>zh-cnTue, 30 Apr 2013 12:57:36 GMTTue, 30 Apr 2013 12:57:36 GMT60- QCore/Library说明文档http://www.shnenglu.com/lwch/archive/2013/04/30/199861.htmllwchlwchTue, 30 Apr 2013 12:24:00 GMThttp://www.shnenglu.com/lwch/archive/2013/04/30/199861.htmlhttp://www.shnenglu.com/lwch/comments/199861.htmlhttp://www.shnenglu.com/lwch/archive/2013/04/30/199861.html#Feedback0http://www.shnenglu.com/lwch/comments/commentRss/199861.htmlhttp://www.shnenglu.com/lwch/services/trackbacks/199861.htmlQCore/Library说明文档
李文?
前言
QCore/Library是一套类STL的类库,它在标准库的范围内删M不常用的heap、deque{结构(臛_我是不常用的Q。ƈZ些容器提供了一些特D的接口Q比如vector中的push_back_unique、add和add_unique{?
Library主要分ؓ六部分,内存调试相关、容器、算法、正则、IO和GraphicQ每个模块都有各自的分工Q他们之间的耦合度极低,几乎每个模块都可以拆出来独立使用Q下面来分别介绍各个模块?
内存调试
我们知道Q在C/C++中内存相关的东西是极难控制的Q在使用不当时可能造成各种错误Q轻则内存泄漏,重则E序崩溃。所以,在生产环境中我们必须通过一个有效的手段来管理好内存。当Ӟ在小块内存频Jnew、delete的过E中也会产生大量的内存碎片,从而导致可用内存数量越来越。应此我们设计了一个内存池来控制小块内存的频繁new、deleteQ以及做到对内存泄漏的检?
在内存池的设计之初,我只是简单的设计Z可以使用的MemoryPool的最版本Q它包含一个大块内存的free_list和每个小块内存的chunk_listQ当时以应付大部分的需求,而且最初用的是Visual Leak Detector来检内存泄漏。但随着旉的推U,惌自己内存泄漏的Ʋ望来强烈,之后便有了一个use_list来保存内存块的释放情c当时完成了q个patch之后Q兴奋的跑了一下TestCaseQ然后的l果我想大家应该知道了,一路的飘红Q到处是内存泄漏?
l过一天的调试Q实在无法容忍的情况下,我翻阅了MSDNQ查Cdbghelp.dll中可以通过许多函数来获取调用堆栈,于是在此之下便生产出了CallStack模块。有了它之后你就可以在Q意地方保存当前的调用堆栈了,真是十分方便。当然直到现在,它还只支持在Windows下调用堆栈的获取Q稍后我会翻阅资料,实现一个like unix的版本,如果可能的话Q?
q里不过多的描述实现的细节,具体可以?a href="http://www.shnenglu.com/lwch/archive/2012/07/14/183420.html">http://www.shnenglu.com/lwch/archive/2012/07/14/183420.html?a href="http://www.shnenglu.com/lwch/archive/2013/01/19/197415.html">http://www.shnenglu.com/lwch/archive/2013/01/19/197415.html两篇文章?
最后来看allocatorQ这里只是简单的为其包装了一层?
template <typename T>
class allocator
{
public:
allocator()
{
}
allocator(const allocator<T>&)
{
}
static T* allocate()
{
MemoryPool* pool = getPool();
return reinterpret_cast<T*>(pool->allocate(sizeof(T), free_handler));
}
static T* allocate(size_t n)
{
MemoryPool* pool = getPool();
return reinterpret_cast<T*>(pool->allocate(n * sizeof(T), free_handler));
}
static void deallocate(T* p)
{
MemoryPool* pool = getPool();
pool->deallocate(p, sizeof(T));
}
static void deallocate(T* p, size_t n)
{
MemoryPool* pool = getPool();
pool->deallocate(p, n * sizeof(T));
}
static void deallocateWithSize(T* p, size_t n)
{
MemoryPool* pool = getPool();
pool->deallocate(p, n);
}
static T* reallocate(T* p, size_t old_size, size_t n)
{
MemoryPool* pool = getPool();
return pool->reallocate(p, old_size, n * sizeof(T), free_handler);
}
public:
static void(*free_handler)(size_t);
static void set_handler(void(*h)(size_t))
{
free_handler = h;
}
protected:
static MemoryPool* getPool()
{
static MemoryPool pool;
return &pool;
}
};
template <typename T>
void (*allocator<T>::free_handler)(size_t) = 0;
容器
容器占了Library的大部分Q容器的作用是用来存储对象的Q容器分为线性和非线性两U。线性的容器有vector、list、string以及用它们作为容器实现的queue、stack四种Q非U性的则有rbtree、hashtable以及用它们作为容器实现的set、map、hashset、hashmap六种。对于每U容器,都必d义出它的value_type、pointer、reference、const_reference、size_type、distance_type、const_iterator、const_reverse_iterator、iterator、reverse_iterator的类型?
所有容器必d含以下几个接口:sizeQ获取容器内元素个数Q、clearQ清I容器)、beginQ获取[first,last)区间中的firstq代器)、endQ获取[first,last)区间中的lastq代器)、rbeginQ获取反向的firstq代器)、rendQ获取反向的lastq代器)?
traits
traits是一U萃取技术,通过它你可以获取某种cd的一些特性,比如是否含有默认构造函数、拷贝构造函数等?
__type_traits
__type_traits用于萃取出某U类型的一些特性,它的原型如下
template <typename T>
struct __type_traits
{
typedef __true_type has_default_construct;
typedef __true_type has_copy_construct;
typedef __true_type has_assign_operator;
typedef __true_type has_destruct;
typedef __false_type is_POD;
};
通过特例化,可以定义出所有类型的q些属性,比如char
template <>
struct __type_traits<char>
{
typedef __true_type has_default_construct;
typedef __true_type has_copy_construct;
typedef __true_type has_assign_operator;
typedef __false_type has_destruct;
typedef __true_type is_POD;
};
__container_traits
__container_traits用于萃取出容器的Ҏ,如上文所说的value_type{特性,它的代码很简?
template <typename T>
struct __container_traits
{
typedef typename T::value_type value_type;
typedef typename T::pointer pointer;
typedef typename T::reference reference;
typedef typename T::const_reference const_reference;
typedef typename T::size_type size_type;
typedef typename T::distance_type distance_type;
typedef typename T::const_iterator const_iterator;
typedef typename T::const_reverse_iterator const_reverse_iterator;
typedef typename T::iterator iterator;
typedef typename T::reverse_iterator reverse_iterator;
};
char_traits
char_traits定义了一些对于Char的操作,包括assignQ赋|、eqQ相{)、ltQ小于)、compareQ比较两个字W串的大)、lengthQ获取字W串的长度)、moveQ移动)、copyQ拷贝)、assignQ字W串赋|、eofQ结束符Q,它的代码比较z,q里不做说明?
type_compare
type_compare用于对两U类型做q行时的匚wQ判断所l定的两U类型是否相同。同样通过特例化技术可以很L的实现它?a href="../Source/QCore/Library/traits.h">代码?
q代?
q代器类是一U类gsmart pointer的东西,一般的它都会支持前|和后置?+?-操作Q有一些特D的q代器同h?=?=操作。当然作ZUsmart pointer不了的?>?操作Q而对于比较操作,则比较的是P代器所保存的倹{?
q代器分为bidirectional_iterator和random_access_iterator两种cdQ前者只支持++?-操作而后者支??q算W,之所以会定义两种cd是ؓ了提高算法的速度。对于一个P代器来说同样需要定义value_type、distance_type、pointer、reference、const_pointer、const_reference的类型?
反向q代?
反向q代器与正向的正好相反,应此我们可以cM的定义它?+为正向P代器?-{运符
iterator_traits
iterator_traits用于萃取P代器的所有特性,应此它比较简?
template <typename Iterator>
struct iterator_traits
{
typedef typename Iterator::value_type value_type;
typedef typename Iterator::distance_type distance_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
typedef typename Iterator::const_pointer const_pointer;
typedef typename Iterator::const_reference const_reference;
};
vector
vector是一U比较常用的容器Q它的内部是一个连l的内存块,应此它有两个接口分别用于获取内存块已使用的大和容器的大,它们是size和capacityQ同样它也有一个reserve接口来调整它的容量。由于vector的内部是q箋的,应此它只允许从后面插入元素,所以它有push_back、push_back_unique和pop_backҎ。当然ؓ了作为queue的容器,我还为其增加了pop_frontҎ用于删除前端的元素。insert和erase用于在某个地Ҏ入和删除元素Qadd和add_unique用于插入另一个vector里的内容Q而unique则会容器内重复的元素删除?
当然vector可以像数l一L使用Q它支持ҎLq算W与at接口来获取某个位|上的元素倹{?
vector容器先介绍到这里,它的代码你可以在QCore/Library/vector.h中找到?
list
list的内部则是一个双向的链表Q应此它在删除前端的元素时会比vector快很多,它的接口基本跟vector相同Q这里就不做q多的介l了。由于vector是内存连l的Q所以它可以直接通过索引来访问某个元素,而list是一个双向的链表Q应此通过制定索引去访问某个元素时会先看这个烦引的值是否小于list长度的一半来军_是从list的头部遍历还是从list的尾部遍历,它的代码你可以在QCore/Library/list.h中找到?
queue和stack
queue是一UFIFOQ先q先出)的结构,应此我们使用list作ؓ它的容器Q通过list的push_back和pop_front可以使代码变的高效。它拥有front和backҎ来获取队列中队头和队元素|同样在插入队列时Q你可以不加选择的直接插入或是插入一个不重复的倹{?
stack是一UFILOQ先q后出)的结构,同样它拥有push、push_unique和popҎ以及top和bottomҎ用于获取栈顶端和底部的元素倹{?
对于q两U结构的代码Q你可以?a href="../Source/QCore/Library/queue.h">QCore/Library/queue.h?a href="../Source/QCore/Library/stack.h">QCore/Library/stack.h中找到?
rbtree
rbtreeQ红黑树Q是一自q的二叉查找树Q应此它拥有较高的查找效率,U黑树有以下5条性质
性质1. 节点是红色或黑色?
性质2. 根节Ҏ黑色?
性质3 每个叶节Ҏ黑色的?
性质4 每个U色节点的两个子节点都是黑色?从每个叶子到根的所有\径上不能有两个连l的U色节点)
性质5. 从Q一节点到其每个叶子的所有\径都包含相同数目的黑色节炏V?
以上摘自癑ֺ癄
对于U黑树的每个节点Q它都拥有它的key和valueQ当然我们是按照节点的key来排序的Q废话)。红黑树拥有insert_equal和insert_uniqueҎ分别用于插入一个节点,前者允许待插入节点的key已存在于q棵树中Q而后者在插入时ƈ不允许这个节点的key已存在于q棵树中Q应此它的返回值则是一个二元的。eraseҎ则提供了对于树中某个节点的删除操作,可以通过q代器或某个key值来删除其中的节炏V?
׃U黑树是一二叉查找树Q应此它应该具备findҎQ用于在树中查找某个节点Q它q回的则是指向这个节点的一个P代器。由于红黑树是有序的Q应此可以通过maximum和minimumҎ得到其中的最大和最倹{通过lower_bound和upper_bound可以得到属于某个key的[first,last]区间Qequal_range是q这个活的,countҎ可以得到某个key所对应的节Ҏ?
rbtree先介绍到这里,E后我会在博客中l箋更新提供更完整的实现ҎQ它的代码你可以?a href="../Source/QCore/Library/rbtree.h">QCore/Library/rbtree.h中找到?
set和map
set是一U集合的l构Q应此在集合中是不允许有重复的元素的Qset是以rbtree作ؓ容器的。应此它的insertҎ对应于rbtree的insert_uniqueҎQ同样rbtree所具备的接口set也同h有,set的key_type与value_type相同Q都是给定的cd?
map则是一Ukey-value的directoryl构Q应此它的key是不允许重复的,map同样是以rbtree作ؓ容器的。应此它的insertҎ同样对应于rbtree的insert_uniqueҎQ在map中除了rbtree的maximum、minimum、lower_bound、upper_bound、equal_range和count没有之外其他接口基本全都拥有Qmap的key_type是给定的cdQ而value_type则是一个以Key和Tl成的二元组?
对于q两U结构的代码Q你可以?a href="../Source/QCore/Library/set.h">QCore/Library/set.h?a href="../Source/QCore/Library/map.h">QCore/Library/map.h中找到?
hashtable
hashtable是一U哈希结构,应此它的元素查找和插入是非常快的。在q里我用的是吊桶法来处理元素插入时的冲突Q当吊桶长度q长Q默认是11个元素)Ӟ会将桶的大小M倍然后重建整个hashtable以提高hashtable中元素的查找速度?
同样的在hashtable中有insert_equal和insert_unique来分别插入允许相同和不同的元素,当遇C个已有的元素时会这个元素插入到W一个与它值相同的节点后面Q这样做的好处是可以单的实现equal_rangeҎ。同时hashtable拥有valueҎ用于通过一个指定的key来查扑ֈ它对应的|findҎ则是用来查找一个key所对应的P代器的。同Lhashtable也拥有countҎ来获取某个key所对应的值的个数Qmaximum和minimum则是用来获取最大值和最值的?
hashtable的代码,你可以在QCore/Library/hashtable.h中找到?
hashset和hashmap
hashset和hashmap基本与set和map相同Q这里不q多做介l,关于它们的代码,你可以在QCore/Library/hashset.h?a href="../Source/QCore/Library/hashmap.h">QCore/Library/hashmap.h中找到?
basic_string
basic_string的实现方式基本和vector差不多,Z提高效率Q在所有的插入操作中若新的长度的一倍小于一个定长(默认?12Q字节时会申h长度的一倍作为容器的定w?
与vector不同的是basic_string拥有c_str和dataҎ用于获取字符指针QappendҎ往字符串尾部链接另一个字W串QassignҎl字W串赋|findҎ查找到第一个指定的字符串的位置Qsubstr则用来获取字W串中一部分的内宏V?
在basic_string中也有format的静态方法来生成一个指定Ş式的字符Ԍ其他用法基本与vecotr相同。它的代码,你可以在QCore/Library/string.h中找到?
本章结
上面介绍了所有的容器的接口和使用ҎQ以及在实现方式上的一些技巧。希望通过上面的介l,读者们能够体会到STLZ么需要这么去设计、这么设计的好处是什么?
在我后来做regex的过E中Q我深刻的体会到Q选用一个合适的数据l构可以l代码的q行效率带来非常大的提升。比如给定NFA某个状态,需要找出所有从q个状态出发的边,之前使用的是mapl构来保存从某个状态出发边的vectorQ之后发现遍历的速度非常~慢Q在换成hashmap之后Q速度有显著的提升。应此在实际~程q程中,选用一个合适的数据l构昑־ؓ重要?
在用线性结构时Q一般在数据量或不q_插入或删除数据的情况下,选用vector作ؓ容器会更快一些。而在需要^凡插入或删除数据的场合下Q选用list作ؓ容器会有更优异的l果。需要保持元素唯一性的情况下,我会优先选用set作ؓ容器Q而在数据量非常大的情况下Q就会用hashset来代替set。map则如它的名字那样Q适用于key-value的场合,而hashmap在数据量非常大的情况下用?
法
min和max函数分别用于求取最值和最大|fill_n用来填充若干个对象的倹{copy_backward用来从后往前拷贝倹{distance用来求给定区间[first, last)的长度。search用于在给定范围[first1, last1)内查扄合序列[first2, last2)集合的倹{swap和iterator_swap分别用于交换两个值和两个q代器指向的倹{sort用于指定范围[first, last)内的值排序,find用于在指定范围[first, last)内查找指定的倹{toArray用于指定范围[first, last)内的D{换ؓ内存q箋的数l,compareArray则用于比较两个数l内的值的个数和值是否相同?
具体的实C码,你可以在QCore/Library/algo.h中找到?
正则
通过重蝲+Q相加)?、|?Q前|)?(前置Q?以及opt函数来生成正则表辑ּ?#949;-NFAQ然后通过buildDFA函数生成DFA。通过parse和match可分析给定的字符串是否符合这个正则表辑ּQparse从头开始跑DFA看这个字W串的开头是否有W合q个正则表达式的字符Ԍ而match则会找[first, last)区间内符合这个正则表辑ּ的第一个字W串的相关结果?
具体的实现方法,你可以看http://www.shnenglu.com/lwch/archive/2013/02/15/197848.html?a href="http://www.shnenglu.com/lwch/archive/2013/02/23/198043.html">http://www.shnenglu.com/lwch/archive/2013/02/23/198043.html两篇文章Q而代码则可在QCore/Library/regex/regex.h中找到?
IO
IO模块包含文gIO和标准输入输出IOQ首先有两个基类分别是basic_istream和basic_ostream分别作ؓ输入和输出stream的基c?其中分别定义了运符>>?lt;<Q通过q两个运符可以直观的看到是从stream输出到变量或是从变量输入到stream中。之后有stdstream_basic和fstream_basic都承自basic_istream和basic_ostream分别作ؓbasic_stdstream和basic_fstream的基c,它们都有open、size、tell、seek、read、write和flushҎQ它们都是对底层C函数的一些封装。而在下一层的basic_stdstream和basic_fstream中则实现了运符>>?lt;<的所有重载,q样做的好处是可以代码更有条理性,q方侉K诅R?
对于标准输入输出来_实际上是对stdin、stdout、stderr的一些封装,当然里面也有一些比较特D的接口Q比如setColorQ用于设|输入输出流的字体颜Ԍ以及一些颜色相关的函数?
对于fstream来说它是针对所有文件的Q应此它会多一个readAll接口用于一ơ性的dq个文g的所有内容,Z节省频繁d盘所造成的性能损耗,我们l它定义了两个buffer用来做cacheQ当你要写入文gӞ它ƈ不是马上q接往盘里写的,而会加到buffer当中Q当辑ֈ一个预值时才真正的写入到磁盘?
那么IO模块先介绍到这里了Q它们的代码你可以在QCore/Library/ios.h?a href="../Source/QCore/Library/istream.h">QCore/Library/istream.h?a href="../Source/QCore/Library/ostream.h">QCore/Library/ostream.h?a href="../Source/QCore/Library/fstream.h">QCore/Library/fstream.h?a href="../Source/QCore/Library/stdstream.h">QCore/Library/stdstream.h?a href="../Source/QCore/Library/iostream.h">QCore/Library/iostream.h?a href="../Source/QCore/Library/iostream.cpp">QCore/Library/iostream.cpp中找到?
l束
上文介绍了大部分模块的实现过E与实现q程中的心得Q让我们来按照顺序回一下?
首先介绍了内存池的实现过E,以及在实现过E中遇到的一些问题,比如如何内存泄漏、如何获取调用堆栈等?
然后介绍了traits技术,它是用来萃取Z些特性用的,通过traits技术你可以得到一个类型是否ؓPOD是否有默认构造函数等Ҏ。而__container_traits则萃取出了容器的一些特性,比如值类型等{。通过char_traits我们得到了一些关于字W串的操作,比如求字W串的长度等?
之后又通过特例化,我们实现了一U比较两个变量类型是否相同的手段?
接下来我们介l了q代器和反向q代器,它们是一U类gsmart pointer的东西,我们可以通过一个[first, last]前闭后开区间来表CZ个容器内的所有元素。然后我们通过iterator_traits萃取Zq代器的一些特性,比如值类型等?
最后我们介l了所有比较常用的容器Q以及在某些场合下应该用哪U容器来辑ֈ高效的目的。比如在数据量较大时使用hashset要比使用set在插入和查找速度要更快一些,而且旉负责度更E_一些?
之后我们又介l了一些常用算法,通过q些法以解决一些简单的问题。比如排序和求取一个范围[first, last]的长度等?
在正则这一章中Q介l了我们是如何通过一些运符的重载来构造出某个正则表达式的状态机的,以及如何通过q行q些状态机来分析给定的字符丌Ӏ?
最后介l了IO模块Q其中分为标准输入输出流和文件流Q通过重蝲<<?gt;>q算W,可以直观的看到是从流中讲内容输入C个变量或是从一个变量中讲内容输入到中?
希望通过本文可读者体会到作者在设计q个库的时候所考虑的问题,以及对这个库有一个大概的认识Q稍后我会在我的博客中补齐所有没有介l过的模块是如何实现的?
修改记录
2013.4.23W一ơ编?
2013.4.24d容器的说?
2013.4.25dhashtable、hashset、hashmap和basic_stringl构的说?
2013.4.28d法和正则的说明
2013.4.30dIO的说明,完结此文

]]>- 山寨STL实现之内存池V2http://www.shnenglu.com/lwch/archive/2013/01/19/197415.htmllwchlwchSat, 19 Jan 2013 12:09:00 GMThttp://www.shnenglu.com/lwch/archive/2013/01/19/197415.htmlhttp://www.shnenglu.com/lwch/comments/197415.htmlhttp://www.shnenglu.com/lwch/archive/2013/01/19/197415.html#Feedback0http://www.shnenglu.com/lwch/comments/commentRss/197415.htmlhttp://www.shnenglu.com/lwch/services/trackbacks/197415.html上一中我们已经实现了一个简单的内存池,可以甌更大块的内存块来减少甌块内存块时产生的内存碎片?br />
在本中Q我们需要ؓ其加入内存泄漏的代码,以此来检代码编写过E中的疏忽带来的内存泄漏。(callstack的显C暂时仅支持WindowsQ?br />
一、内存泄漏检?/strong>
首先Q改写obj和blockl构Q在obj中加入一个域released表示q个chunk是否被释?br />
1 struct obj
2 {
3 #ifdef _DEBUG
4 bool released;
5
6 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) // Only windows can get callstack
7 #define CALLSTACK_MAX_DEPTH 30
8 UINT_PTR callStack[CALLSTACK_MAX_DEPTH];
9 DWORD dwCallStackDepth; // Real depth
10 #endif
11
12 #endif
13 obj* next;
14 };
15
16 struct block
17 {
18 block* next;
19 void* data;
20 #ifdef _DEBUG
21 size_type size;
22 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
23 UINT_PTR callStack[CALLSTACK_MAX_DEPTH];
24 DWORD dwCallStackDepth;
25 #endif
26 #endif
27 };
其中的callstack部分在下一节中介绍
然后Q我们增加一个结?br />
1 #ifdef _DEBUG
2 struct use
3 {
4 obj* data;
5 use* next;
6 };
7 #endif
其中data域指向了一块分配出ȝ内存块Qnext域Ş成了一张链表?br />
然后Q我们添加一个成员变量来保存q张链表Q以及一个函数来一个chunk插入q张链表
#ifdef _DEBUG
use* use_list;
#endif
#ifdef _DEBUG
inline void MemoryPool::addUseInfo(obj* ptr)
{
use* p = (use*)malloc(sizeof(use));
p->data = ptr;
p->next = use_list;
use_list = p;
}
#endif
然后Q我们来改写refill函数使其在分配内存块时打上released标记Qƈ每个分配的内存块记录下?br />
1 void* MemoryPool::refill(int i, void(*h)(size_type))
2 {
3 const int count = 20;
4 const int preSize = (i + 1) * ALIGN + headerSize;
5 char* p = (char*)malloc(preSize * count);
6 while(p == 0)
7 {
8 h(preSize * count);
9 p = (char*)malloc(preSize * count);
10 }
11 block* pBlock = (block*)malloc(sizeof(block));
12 while(pBlock == 0)
13 {
14 h(sizeof(block));
15 pBlock = (block*)malloc(sizeof(block));
16 }
17 pBlock->data = p;
18 pBlock->next = free_list;
19 free_list = pBlock;
20
21 obj* current = (obj*)p;
22 #ifdef _DEBUG
23 addUseInfo(current);
24 current->released = false;
25 #endif
26 current = (obj*)((char*)current + preSize);
27 for(int j = 0; j < count - 1; ++j)
28 {
29 #ifdef _DEBUG
30 addUseInfo(current);
31 current->released = true;
32 #endif
33 current->next = chunk_list[i];
34 chunk_list[i] = current;
35 current = (obj*)((char*)current + preSize);
36 }
37 return (char*)p + headerSize;
38 }
其中的headerSize跟callstack有关Q将在下一节中介绍?br />
当然Q在deallocate时要此内存块的released标记打ؓtrue
1 void MemoryPool::deallocate(void* p, size_type n)
2 {
3 if(p == 0) return;
4 if(n > MAX_BYTES)
5 {
6 free(p);
7 return;
8 }
9 const int i = INDEX(ROUND_UP(n));
10 #ifdef _DEBUG
11 p = (char*)p - (int)headerSize;
12 obj* ptr = reinterpret_cast<obj*>(p);
13 if (ptr->released) throw error<char*>("chunk has already released", __FILE__, __LINE__);
14 ptr->released = true;
15 #endif
16 reinterpret_cast<obj*>(p)->next = chunk_list[i];
17 chunk_list[i] = reinterpret_cast<obj*>(p);
18 }
OKQ现在已l有模有样了Q可以松口气了。接下来是最重要的部分,在MemoryPool析构时检这个Pool内的use_list中是否有chunk的released标记为trueQ内存泄漏了Q?br />
1 MemoryPool::~MemoryPool()
2 {
3 #ifdef _DEBUG
4 while (use_list)
5 {
6 use *ptr = use_list, *next = use_list->next;
7 if (!ptr->data->released)
8 {
9 obj* pObj = ptr->data;
10 Console::SetColor(true, false, false, true);
11 throw error<char*>("chunk leaked", __FILE__, __LINE__);
12 }
13 free(ptr);
14 use_list = next;
15 }
16 #endif
17 clear();
18 }
其实说来也容易,只需要检每个chunk的released标记是否为truep了,而最后的clear函数是以前析构函数的代码Q用来释放所有申Lblock和大块的chunk?br />
OKQ现在我们已l可以检出没有被deallocate的chunk了?br />
二、callstack
首先Q我们先来看一个Windows APIQ?#8220;CaptureStackBackTrace”q个API通过传入的一个数l来得到一l地址。当然有q个APIq不够,我们q需要知道是哪个文g的第几行?#8220;SymGetSymFromAddr64”q个API用来获取某个地址对应的函数名Q?#8220;SymGetLineFromAddr64”q个API则是用来获取某个地址对应的文件名和行LQ这两个函数?2位版本则是不?4的。有了这些Windows APIQ我们就可以很轻杄获取到当前函数的调用堆栈了,主要的功劌是要归功于Windows强大的dbghelp?br />
最后,完整的代码你可以?a target="_blank">http://code.google.com/p/qlanguage/中找到?img src ="http://www.shnenglu.com/lwch/aggbug/197415.html" width = "1" height = "1" />
]]> - 如何比较两个变量的类型是否相?/title>http://www.shnenglu.com/lwch/archive/2012/08/25/188253.htmllwchlwchSat, 25 Aug 2012 08:13:00 GMThttp://www.shnenglu.com/lwch/archive/2012/08/25/188253.htmlhttp://www.shnenglu.com/lwch/comments/188253.htmlhttp://www.shnenglu.com/lwch/archive/2012/08/25/188253.html#Feedback0http://www.shnenglu.com/lwch/comments/commentRss/188253.htmlhttp://www.shnenglu.com/lwch/services/trackbacks/188253.html template <typename T1, typename T2>
inline const bool compare_type(T1, T2)
{
return false;
}
template <>
inline const bool compare_type(int, int)
{
return true;
}
template <>
inline const bool compare_type(float, float)
{
return true;
}
template <>
inline const bool compare_type(double, double)
{
return true;
}
template <>
inline const bool compare_type(char, char)
{
return true;
}
template <>
inline const bool compare_type(wchar_t, wchar_t)
{
return true;
}
template <typename T1, typename T2>
inline const bool compare_type(T1*, T2*)
{
return compare_type(T1(), T2());
}
template <typename T1, typename T2>
inline const bool compare_type(const T1*, const T2*)
{
return compare_type(T1(), T2());
}
template <typename T1, typename T2>
inline const bool compare_type(const T1*, T2*)
{
return false;
}
template <typename T1, typename T2>
inline const bool compare_type(T1*, const T2*)
{
return false;
}
通过特例化,我们可以很轻杄查看两个变量的类型是否相同?img src ="http://www.shnenglu.com/lwch/aggbug/188253.html" width = "1" height = "1" />
]]> - 山寨STL实现之listhttp://www.shnenglu.com/lwch/archive/2012/08/09/186770.htmllwchlwchThu, 09 Aug 2012 13:17:00 GMThttp://www.shnenglu.com/lwch/archive/2012/08/09/186770.htmlhttp://www.shnenglu.com/lwch/comments/186770.htmlhttp://www.shnenglu.com/lwch/archive/2012/08/09/186770.html#Feedback0http://www.shnenglu.com/lwch/comments/commentRss/186770.htmlhttp://www.shnenglu.com/lwch/services/trackbacks/186770.html
因ؓ是双向链表的关系Q那么必然有一U结构来表示链表中的节点?br />
template <typename T>
struct __list_node
{
__list_node<T>* prev;
__list_node<T>* next;
T data;
__list_node() : prev(NULL), next(NULL)
{
}
__list_node(const T& x) : prev(NULL), next(NULL), data(x)
{
}
};
然后我们定义出其iterator和const_iterator的结?br />
template <typename T>
struct __list_iterator
{
typedef __list_iterator<T> iterator;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
typedef const T* const_pointer;
typedef const T& const_reference;
typedef __list_node<T>* link_type;
typedef void* void_pointer;
link_type node;
__list_iterator(link_type x) : node(x)
{
}
__list_iterator(const __list_const_iterator<T>& x) : node(x.node)
{
}
__list_iterator() : node(NULL)
{
}
inline iterator& operator++()
{
node = ((link_type)node)->next;
return *this;
}
inline iterator operator++(int)
{
iterator tmp = *this;
++*this;
return tmp;
}
inline iterator& operator--()
{
node = ((link_type)node)->prev;
return *this;
}
inline iterator operator--(int)
{
iterator tmp = *this;
--*this;
return tmp;
}
inline reference operator*()const
{
return node->data;
}
inline bool operator==(const iterator& x)const
{
return node == x.node;
}
inline bool operator!=(const iterator& x)const
{
return node != x.node;
}
};
׃const_iterator与iterator的结构类|q里不再贴出。其中重载了++?-q算W,实际上就是节点的前后Ud?br />
然后看一下list的定?br />
template <typename T>
class list
{
}
让我们来看看list中的别名
public:
typedef T value_type;
typedef T* pointer;
typedef __list_iterator<T> iterator;
typedef __list_const_iterator<T> const_iterator;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef reverse_iterator<const_iterator, value_type, size_type, difference_type> const_reverse_iterator;
typedef reverse_iterator<iterator, value_type, size_type, difference_type> reverse_iterator;
下面是其内部的成员变?br />
protected:
typedef __list_node<T>* link_type;
typedef list<T> self;
typedef allocator<__list_node<T> > Node_Alloc;
link_type node;
size_type length;
在STL中从begin到endL以一个前闭后开的Ş式来表示的,应此我们l出一个node节点来表Cend所指位|,而node节点的前驱则是这个list的v始节点,而length则存储了q个list的元素数量?br />
下面来看看list中最基本的构造函数和析构函数
list() : length(0)
{
node = Node_Alloc::allocate();
node->next = node;
node->prev = node;
}
~list()
{
clear();
Node_Alloc::deallocate(node);
}
在list对象构造之初,首先构造出node节点Q其的前驱和后l都指向其本w,应此通过begin和end拿出的P代器为同一个。在list对象析构Ӟ首先这个list清空Q然后将构造出的node节点释放掉?br />
然后是其begin和endҎQ用来获取第一个元素和最后一个元素的后一个元素的q代?br />
inline iterator begin()
{
return node->next;
}
inline const_iterator begin()const
{
return node->next;
}
inline iterator end()
{
return node;
}
inline const_iterator end()const
{
return node;
}
front和back分别被用来获取第一个元素和最后一个元?br />
inline reference front()
{
return *begin();
}
inline const_reference front()const
{
return *begin();
}
inline reference back()
{
return *end();
}
inline const_reference back()const
{
return *end();
}
empty、size分别被用来判别容器是否ؓI、获取容器内元素的个?br />
inline bool empty()const
{
return length == 0;
}
inline size_type size()const
{
return length;
}
list与vector不同的是list是双向的Q应此它允许从头两个方向来插入和删除元?br />
inline void push_front(const T& x)
{
insert(begin(), x);
}
inline void push_back(const T& x)
{
insert(end(), x);
}
inline void pop_front()
{
erase(begin());
}
inline void pop_back()
{
erase(--end());
}
然后我们来看一下push的本质,insert函数
iterator insert(const iterator& position, const T& x)
{
if(!inRange(position)) throw "out of range";
link_type tmp = Node_Alloc::allocate();
construct(tmp, x);
tmp->prev = position.node->prev;
tmp->next = position.node;
position.node->prev->next = tmp;
position.node->prev = tmp;
++length;
return tmp;
}
q里首先会检查这个P代器是否属于q个listQ然后构造出一个新节点Qƈ把它插入到这个P代器的前面,最后将节点?1?br />
然后是其删除节点函数erase
void erase(const iterator& position)
{
if(!inRange(position)) throw "out of range";
position.node->prev->next = position.node->next;
position.node->next->prev = position.node->prev;
destruct(&position.node->data, has_destruct(position.node->data));
Node_Alloc::deallocate(position.node);
--length;
}
q里同样会检查这个P代器是否属于q个listQ然后将q个节点U除Q最后析构ƈ释放内存I间?br />
最后让我们来看一下list中重载的q算W?br />
self& operator=(const self& x)
{
if(this == &x) return *this;
iterator first1 = begin();
iterator last1 = end();
const_iterator first2 = x.begin();
const_iterator last2 = x.end();
while (first1 != last1 && first2 != last2) *first1++ = *first2++;
if (first2 == last2) erase(first1, last1);
else insert(last1, first2, last2);
return *this;
}
reference operator[](size_type n)
{
if(n < 0 || n >= length) throw "out of range";
link_type current = NULL;
if(n < length / 2)
{
current = node->next;
for(size_type i = 0; i < n; i++, current = current->next);
}
else
{
n = length - n - 1;
current = node->prev;
for(size_type i = 0; i < n; i++, current = current->prev);
}
return current->data;
}
inline value_type at(size_type n)
{
return operator[](n);
}
因ؓ其内部用的是双向链表,应此通过指定下标来获取这个元素是可分别从两头q行Ud指针?br />
xQlist的讲解已完成Q完整代码请?a target="_blank">http://qlanguage.codeplex.com下蝲
]]> - 山寨STL实现之内存池http://www.shnenglu.com/lwch/archive/2012/07/14/183420.htmllwchlwchSat, 14 Jul 2012 10:40:00 GMThttp://www.shnenglu.com/lwch/archive/2012/07/14/183420.htmlhttp://www.shnenglu.com/lwch/comments/183420.htmlhttp://www.shnenglu.com/lwch/archive/2012/07/14/183420.html#Feedback1http://www.shnenglu.com/lwch/comments/commentRss/183420.htmlhttp://www.shnenglu.com/lwch/services/trackbacks/183420.html
减少内存片Q提高性能?br />
首先不得不提的是Win32和x64中对于指针的长度是不同的Q?strong>在Win32中一个指针占4字节Q而在x64中一个指针占8字节。也正是不清楚这一点,当我在x64中将指针作ؓ4字节修改造成其他数据异常?br />
首先我们先来定义三个?br />
#define ALIGN sizeof(void*)
#define MAX_BYTES 128
#define MAX_COUNT (MAX_BYTES / ALIGN)
正如前面所说的Qؓ了兼容Win32与x64应此我们要甌的内存按void*的大来寚w。正如前面所说的Q我们认为小?28字节的内存ؓ内存,会生内存碎片,应此在申h应该劲量甌一块较大的内存而将其中的一块分配l他?br />
然后让我们来看一下内存池中的成员变量
struct obj
{
obj* next;
};
struct block
{
block* next;
void* data;
};
obj* chunk_list[MAX_COUNT];
size_t chunk_count;
block* free_list;
q里使用objl构来存储已释放内存的列表,q样做的好处是可以更节省内存。在Win32中用这块内存的?字节来指向下一个节点,而在x64中用这块内存的?字节来指向下一个节炏V?br />
chunk_listQ保存通过deallocate或refill中释放或是新甌的内存块列表Qdeallocate和refill会在下文中介绍?br />
chunk_countQ内存块列表中已有的内存块数量?br />
free_listQ保存了通过malloc甌内存块的链表?br />
下面我们来看一下内存池的构造函C析构函数
MemoryPool() : free_list(0), chunk_count(0)
{
for(int i = 0; i < MAX_COUNT; ++i) chunk_list[i] = 0;
}
~MemoryPool()
{
block* current = free_list;
while(current)
{
block* next = current->next;
free(current->data);
free(current);
current = next;
}
}
构造函C初始化free_list和chunk_count?Qƈ初始化chunk_listZ个空列表。而在析构函数中我们必释放每一块通过malloc甌的大内存块?br />
接下来是内存的申?br />
template <typename T>
T* allocate(size_t n, void(*h)(size_t))
{
if(n == 0) return 0;
if(n > MAX_BYTES)
{
T* p = (T*)malloc(n);
while(p == 0)
{
h(n);
p = (T*)malloc(n);
}
return p;
}
const int i = INDEX(ROUND_UP(n));
obj* p = chunk_list[i];
if(p == 0)
{
return refill<T>(i, h);
}
chunk_list[i] = p->next;
return reinterpret_cast<T*>(p);
}
值得注意的是Q在调用时必M入一个函数指针作为参敎ͼ当malloc甌内存p|时会去调用这个函数来释放够多的内存空间。当要申L内存大小过128字节Ӟ调用默认的malloc为其甌内存。否则先查找列表中是否还有够的I间分配l它Q若已没有够的I间分配l它Q则调用refill甌一块大内存?br />
然后是内存释攑և数deallocate
template <typename T>
void deallocate(T* p, size_t n)
{
if(p == 0) return;
if(n > MAX_BYTES)
{
free(p);
return;
}
const int i = INDEX(ROUND_UP(n));
reinterpret_cast<obj*>(p)->next = chunk_list[i];
chunk_list[i] = reinterpret_cast<obj*>(p);
}
值得注意的是在释放时必须l出q块内存块的大小。若q块内存大于128字节Ӟ调用默认的free函数释放掉这块内存。否则将其加到对应的chunk_list列表内?br />
然后是调整内存块大小函数reallocate
template <typename T>
T* reallocate(T* p, size_t old_size, size_t new_size, void(*h)(size_t))
{
if(old_size > MAX_BYTES && new_size > MAX_BYTES)
{
return realloc(p, new_size);
}
if(ROUND_UP(old_size) == ROUND_UP(new_size)) return p;
T* result = allocate<T>(new_size, h);
const size_t copy_size = new_size > old_size ? old_size : new_size;
memcpy(result, p, copy_size);
deallocate<T>(p, old_size);
return result;
}
参数中必ȝ块内存的原始大小和要调整后的大小Q同时也必须l出当内存不x的释攑և数的指针。若旧内存块和新内存块的大小都大?28字节Ӟ调用默认的realloc函数重新分配内存。否则先按调整后的大申请一块内存,q把原来的内Ҏ贝过来,最后释放掉原来的内存块。这里ƈ不徏议用这个函敎ͼ而是手动的去重新甌内存q拷贝释放?br />
然后来看4个非常简单的计算函数
inline size_t ROUND_UP(size_t bytes)const
{
return (bytes + ALIGN - 1) & ~(ALIGN - 1);
}
inline size_t ROUND_DOWN(size_t bytes)const
{
return bytes & ~(ALIGN - 1);
}
inline int INDEX(size_t bytes)const
{
return (bytes + ALIGN - 1) / ALIGN - 1;
}
inline size_t obj_count(int i)const
{
size_t result = 0;
obj* current = chunk_list[i];
while(current)
{
++result;
current = current->next;
}
return result;
}
?个用于内存对齐和计算索引Q最后一个用于获取一在空闲列表内一个内存块的数量?br />
然后是refill函数Q用于在没有I闲内存块时甌一块大内存?br />
template <typename T>
T* refill(int i, void(*h)(size_t))
{
const int count = 20;
const int preSize = (i + 1) * ALIGN;
char* p = (char*)malloc(preSize * count);
while(p == 0)
{
h(preSize * count);
p = (char*)malloc(preSize * count);
}
block* pBlock = (block*)malloc(sizeof(block));
while(pBlock == 0)
{
h(sizeof(block));
pBlock = (block*)malloc(sizeof(block));
}
pBlock->data = p;
pBlock->next = free_list;
free_list = pBlock;
obj* current = (obj*)(p + preSize);
for(int j = 0; j < count - 1; ++j)
{
current->next = chunk_list[i];
chunk_list[i] = current;
current = (obj*)((char*)current + preSize);
}
chunk_count += count - 1;
rebalance();
return reinterpret_cast<T*>(p);
}
首先甌一个大内存块,然后这块申请到的内存块攑օfree_list链表内,最后组lvchunk_list中对应内存卡块的链表Q然后重新调整chunk_list列表Q最后将甌到的内存块返回?br />
最后来看一下调整函数rebalance
void rebalance()
{
for(int i = MAX_COUNT - 1; i > 0; --i)
{
const size_t avge = chunk_count / MAX_COUNT;
size_t count = obj_count(i);
if(count > avge)
{
const int preSize = ROUND_DOWN((i + 1) * ALIGN / 2);
const int j = INDEX(preSize);
for(int k = count; k > avge; --k)
{
obj* chunk = chunk_list[i];
chunk_list[i] = chunk_list[i]->next;
if(i % 2 == 1)
{
chunk->next = (obj*)((char*)chunk + preSize);
chunk->next->next = chunk_list[j];
chunk_list[j] = chunk;
}
else
{
chunk->next = chunk_list[j];
chunk_list[j] = chunk;
obj* next = (obj*)((char*)chunk + preSize);
next->next = chunk_list[j + 1];
chunk_list[j + 1] = next;
}
++chunk_count;
}
}
}
}
q里从后臛_查看对应内存块空闲链表的长度Q若过q_数量Q则其切分?块较的内存块放入对应的链表内。这样做的好处是可以形成一个金字塔形的分布状况Q既小的内存块大小拥有的节Ҏ量越多,正如本文开头所_使用内存池是Z解决在申请小块内存时造成的内存碎片?br />
xQ内存池的讲解已完成Q完整的代码请到http://qlanguage.codeplex.com下蝲
]]> - 山寨STL实现之vectorhttp://www.shnenglu.com/lwch/archive/2012/06/17/179179.htmllwchlwchSun, 17 Jun 2012 09:08:00 GMThttp://www.shnenglu.com/lwch/archive/2012/06/17/179179.htmlhttp://www.shnenglu.com/lwch/comments/179179.htmlhttp://www.shnenglu.com/lwch/archive/2012/06/17/179179.html#Feedback4http://www.shnenglu.com/lwch/comments/commentRss/179179.htmlhttp://www.shnenglu.com/lwch/services/trackbacks/179179.html
template <typename T>
class vector
{
};
让我们先来看看vector中的一些别?br />
public:
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef const T* const_iterator;
typedef reverse_iterator<const_iterator, value_type, size_type, difference_type> const_reverse_iterator;
typedef T* iterator;
typedef reverse_iterator<iterator, value_type, size_type, difference_type> reverse_iterator;
׃可见Q正?a href="http://www.shnenglu.com/lwch/archive/2012/06/02/177248.html">上一?/a>所_vector的P代器是由原生的指针来实现的?br />
下面是其内部的成员变?br />
protected:
typedef vector<T> self;
typedef allocator<T> Alloc;
iterator start;
iterator finish;
iterator end_of_element;
startQ指向vector的v始地址
finishQ指向最后一个元素的后一个元素的地址
end_of_elementQ指向已甌内存块的l束位置QfinishL于或等于end_of_elementQ?br />
在STL中从begin到endL以一个前闭后开的Ş式来表示的,形如[begin,end)Q这样做的好处是可以使代码写的更z:
template <typename Iterator, typename T>
Iterator find(Iterator first, Iterator last, const T& value)
{
while(first != last && *first != value) ++first;
return first;
}
下面来看看vector中最基本的构造函数和析构函数
vector() : start(0), finish(0), end_of_element(0)
{
}
~vector()
{
destruct(start, end_of_element);
if (start != 0) Alloc::deallocate(start, end_of_element - start);
}
q里其中的3个成员变量都填充??br />
?a href="http://www.shnenglu.com/lwch/archive/2012/06/02/177248.html">上一?/a>所_在STL中是内存分配与对象初始化分开的,同样对象析构与内存释放也是被分开的?br />
然后是其begin和endҎQ用来获取第一个元素和最后一个元素的后一个元素的q代器?br />
inline iterator begin()
{
return start;
}
inline const_iterator begin()const
{
return start;
}
inline iterator end()
{
return finish;
}
inline const_iterator end()const
{
return finish;
}
front和back分别被用来获取第一个元素和最后一个元?br />
inline reference front()
{
return *begin();
}
inline const_reference front()const
{
return *begin();
}
inline reference back()
{
return *(end() - 1);
}
inline const_reference back()const
{
return *(end() - 1);
}
empty、size、capacity分别被用来判别容器是否ؓI、容器内元素的个数和容器的大?br />
inline bool empty()const
{
return begin() == end();
}
inline const size_type size()const
{
return size_type(end() - begin());
}
inline const size_type capacity()const
{
return size_type(end_of_element - begin());
}
׃在vector的头部插入元素会使所有元素后U,应此它被设计为单向的Q只能由N插入或移除数?br />
void push_back(const T& x)
{
if (end_of_element != finish)
{
construct(&*finish, x);
++finish;
}
else
{
insert_aux(end(), x);
}
}
inline void pop_back()
{
--finish;
destruct<T>(finish, has_destruct(*finish));
}
当然从头部移除数据也q不可以,可以使用eraseҎ来移除头部的数据QeraseҎ会在后面的部分作出说明?br />
我们先来看一下insert_auxq个ҎQ在插入元素时几乎都使用Cq个Ҏ?br />
void insert_aux(const iterator position, const T& x)
{
if(finish != end_of_element)
{
construct(&*finish, *(finish - 1));
T x_copy = x;
copy_backward(position, finish - 1, finish);
*position = x_copy;
++finish;
}
else
{
const size_type old_size = size();
const size_type new_size = old_size == 0 ? 2 : old_size * 2;
iterator tmp = Alloc::allocate(new_size);
uninitialized_copy(begin(), position, tmp);
iterator new_position = tmp + (position - begin());
construct(&*new_position, x);
uninitialized_copy(position, end(), new_position + 1);
destruct(begin(), end());
Alloc::deallocate(begin(), old_size);
end_of_element = tmp + new_size;
finish = tmp + old_size + 1;
start = tmp;
}
}
在容器还有够的I间Ӟ首先从position位置到finish位置的元素整体后UM个位|,最后将要被插入的元素写入到原position的位|同时改变finish指针的倹{?br />
若空间不xQ首先根据原有空间的大小的一倍来甌内存Q然后将元素从原有位|的begin到position拯到新甌的内存中Q然后在新申请内存的指定位置插入要插入的元素|最后将余下的部分也拯q来。然后将原有元素析构掉ƈ把内存释放掉?br />
Z不?/span>reallocate?
reallocate的本意ƈ不是在原有内存的位置增加或减内存,reallocate首先会试囑֜原有的内存位|增加或减少内存Q?strong>若失败则会重新申请一块新的内存ƈ把原有的数据拯q去Q这U操作本质上{h于重新申请一块内存,应此q里使用的是allocate而ƈ非reallocate?br />
然后让我们来看一下insert和eraseҎ
inline iterator insert(iterator position, const T& x)
{
const size_type pos = position - begin();
if(finish != end_of_element && position == end())
{
construct(&*finish, x);
++finish;
}
else insert_aux(position, x);
return begin() + pos;
}
iterator erase(iterator position)
{
destruct(position, has_destruct(*position));
if (position + 1 != end())
{
copy(position + 1, end(), position);
}
--finish;
return position;
}
若是要在最后插入一个元素且容器的剩余空间还_的话Q直接将元素插入到finish的位|,q将finish指针后移一位即可。若容器I间不够或不是插在最后一个的位置Q则调用insert_aux重新分配内存或插入?br />
删除旉先析构掉原有元素Q若被删元素不是最后一个元素,则将后面的所有元素拷贝过来,最后将finish指针前移一个位|?br />
最后让我们来看一下其中重载的q算W?br />
self& operator=(const self& x)
{
if(&x == this) return *this;
size_type const other_size = x.size();
if(other_size > capacity())
{
destruct(start, finish);
Alloc::deallocate(start, capacity());
start = Alloc::allocate(other_size);
finish = uninitialized_copy(x.begin(), x.end(), start);
end_of_element = start + other_size;
}
else
{
finish = uninitialized_copy(x.begin(), x.end(), start);
}
return *this;
}
inline reference operator[](size_type n)
{
return *(begin() + n);
}
inline value_type at(size_type n)
{
return *(begin() + n);
}
׃vector内部用的是原生的指针Q应此这些运符的用方式和原生指针的ƈ无差异?strong>值得注意的是在做赋值操作时会生内存的重新分配与拷贝操作?br />
xQvector的讲解已完成Q完整的代码请到http://qlanguage.codeplex.com下蝲
]]> - 山寨STL实现之traits,construct&destructhttp://www.shnenglu.com/lwch/archive/2012/06/02/177248.htmllwchlwchSat, 02 Jun 2012 14:39:00 GMThttp://www.shnenglu.com/lwch/archive/2012/06/02/177248.htmlhttp://www.shnenglu.com/lwch/comments/177248.htmlhttp://www.shnenglu.com/lwch/archive/2012/06/02/177248.html#Feedback3http://www.shnenglu.com/lwch/comments/commentRss/177248.htmlhttp://www.shnenglu.com/lwch/services/trackbacks/177248.html
先来看看STL中最基本的对象iterator
template <typename T, typename Size = size_t, typename Difference = ptrdiff_t>
struct iterator
{
typedef T value_type;
typedef Difference difference_type;
typedef T* pointer;
typedef T& reference;
typedef const T* const_pointer;
typedef const T& const_reference;
typedef iterator<T, Size, Difference> self;
};
template <typename T, typename Size = size_t, typename Difference = ptrdiff_t>
struct const_iterator : public iterator<T>
{
};
׃上代码可知,对于每一个iterator必须定义其value_type,size_type,difference_type,pointer,reference,const_pointer,const_reference和selfcd?nbsp;
一、value_type
value_type指示了该q代器所保存的值类?br />
二、difference_type
difference_type用来指示两个q代器之间的距离cd
三、pointer,reference,const_pointer,const_reference
分别是所指之物的指针,引用,指针帔R和引用常量的cd
四、self
selfq代器自w的cd
下面来看一下iterator_traitsQiterator_traits主要用来萃取q代器iterator的值类型等
template <typename Iterator>
struct iterator_traits
{
typedef typename Iterator::value_type value_type;
typedef typename Iterator::difference_type difference_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
typedef typename Iterator::const_pointer const_pointer;
typedef typename Iterator::const_reference const_reference;
typedef typename Iterator::self self_type;
};
q里有一点可以提前预告一下,vector作ؓ一个容器,其内部是使用指针作ؓq代器的Q那么我们如何萃取出它的值类型等呢?
{案很简单,特例化,那么我们来为iterator_traits分别做两UT*和const T*的特例化
template <typename T>
struct iterator_traits<T*>
{
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
typedef const T* const_pointer;
typedef const T& const_reference;
typedef T* self_type;
};
template <typename T>
struct iterator_traits<const T*>
{
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
typedef const T* const_pointer;
typedef const T& const_reference;
typedef const T* self_type;
};
xQ我们可以用iterator_traits萃取出每Uiterator的值类型等内容了?br />
之前已经说到了,通过traits可以萃取Z些对象的Ҏ,从而提高代码的效率。事实确实如此,通过traits可萃取出一个对象是否是一个POD对象Q对于一个POD对象Q我们在拯Ӟ不应该用其拯构造函数或是operator=Q而用memcpy则效率更高?br />
下面我们来看一下__type_traits
struct __true_type
{
};
struct __false_type
{
};
template <typename I>
struct __type_traits
{
typedef __false_type has_default_construct;
typedef __false_type has_copy_construct;
typedef __false_type has_assign_operator;
typedef __false_type has_destruct;
typedef __false_type is_POD;
};
不得不提的是其中分别用__true_type和__false_type来表C是否存在这个特性?br />
那么我们如何萃取出基cd诸如int,char{的Ҏ呢Q?br />
{案依然是特例化Q这里代码不再脓出,文末会给出完整代码的详细地址?br />
最后我们用一个hash_destruct的函数来获取个类型是否有析构函数?br />
template <typename T>
inline auto has_destruct(const T&)->decltype(static_cast<__type_traits<T>::has_destruct*>(0))
{
return static_cast<typename __type_traits<T>::has_destruct*>(0);
}
template <typename T>
inline auto has_destruct(T*)->decltype(static_cast<__type_traits<T>::has_destruct*>(0))
{
static_assert(false, "Please use const T& not T*");
return static_cast<typename __type_traits<T>::has_destruct*>(0);
}
template <typename T>
inline auto has_destruct(const T*)->decltype(static_cast<__type_traits<T>::has_destruct*>(0))
{
static_assert(false, "Please use const T& not const T*");
return static_cast<typename __type_traits<T>::has_destruct*>(0);
}
不得不提的是C++0x的确很强大,可以通过形参来确定返回值的cdQ这h们就可以萃取个类型的has_destruct域是__true_type或是__false_type了?br />
最后来看看construct和destruct的代码,在STL中对象的内存分配和构造是被分开的,对于基础对象int,char{,在析构时我们无需调用其析构函数?br />
下面来看construct和destruct的实?br />
template <typename T1, typename T2>
inline void construct(T1* p, const T2& value)
{
new (p) T1(value);
}
template <typename T>
inline void destruct(T* p, __true_type*)
{
p->~T();
}
template <typename T>
inline void destruct(T*, __false_type*)
{
}
template <typename ForwardIterator>
inline void destruct(ForwardIterator first, ForwardIterator last)
{
while(first != last)
{
destruct(first, has_destruct(*first));
++first;
}
}
xQ关于traits技术和construct及destruct的讲解已完成Q完整的代码请到http://qlanguage.codeplex.com/下蝲
]]> - 山寨STL实现之allocatorhttp://www.shnenglu.com/lwch/archive/2012/05/20/175498.htmllwchlwchSun, 20 May 2012 13:45:00 GMThttp://www.shnenglu.com/lwch/archive/2012/05/20/175498.htmlhttp://www.shnenglu.com/lwch/comments/175498.htmlhttp://www.shnenglu.com/lwch/archive/2012/05/20/175498.html#Feedback0http://www.shnenglu.com/lwch/comments/commentRss/175498.htmlhttp://www.shnenglu.com/lwch/services/trackbacks/175498.html作ؓ一个山寨的STLQ那么不得不提的是其中的allocatorQ空间配|器Q。顾名思义Q它是负责空间分配用的,下面代码十分单,仅对C函数malloc和freeq行了薄薄的一层封装,同时l定了自定义函数free_handler用于在空间分配时候由于内存被占满了而导致的分配p|?br />
q里值得注意的是Q这个释攑և数的函数指针应该是由调用Ҏ负责指定Qƈ且它仅有一个参数表明至需要释攑֤字节的内存?br />
下面来看代码Q代码非常简单,应此q里׃逐一展开说明了?
#ifndef _QLANGUAGE_LIBRARY_ALLOC_H_
#define _QLANGUAGE_LIBRARY_ALLOC_H_

#if 0
#include <new>
#define __THROW_BAD_ALLOC throw std::bad_alloc
#elif !defined(__THROW_BAD_ALLOC)
#include <iostream>
#define __THROW_BAD_ALLOC std::cerr << "out of memory" << std::endl; exit(1)
#endif

namespace QLanguage


{
namespace Library

{
template <typename T>
class allocator

{
public:
allocator()

{
}

allocator(const allocator<T>&)

{
}

static T* allocate()

{
const size_t size = sizeof(T);
T* result = (T*)malloc(size);
while(result == nullptr)

{
if(free_handler) free_handler(size);
else __THROW_BAD_ALLOC;
result = (T*)malloc(size);
}
return result;
}

static T* allocate(const size_t& n)

{
const size_t size = n * sizeof(T);
if(size <= 0) throw "bad allocate size";
T* result = (T*)malloc(size);
while(result == nullptr)

{
if(free_handler) free_handler(size);
else __THROW_BAD_ALLOC;
result = (T*)malloc(size);
}
return result;
}

static void deallocate(T* p)

{
free(p);
}

static void deallocate(T* p, const size_t&)

{
free(p);
}

static T* reallocate(T* p, const size_t& n)

{
const size_t size = n * sizeof(T);
if(size <= 0) throw "bad reallocate size";
T* result = (T*)realloc(p, size);
while(result == nullptr)

{
if(free_handler) free_handler(size);
else __THROW_BAD_ALLOC;
result = (T*)realloc(p, size);
}
return result;
}
public:
static void(*free_handler)(const size_t&);

static void set_handler(void(*h)(const size_t&))

{
free_handler = h;
}
};

template <typename T>
typename void (*allocator<T>::free_handler)(const size_t&) = nullptr;
}
}

#endif
完整代码请到http://qlanguage.codeplex.com/下蝲
]]> - 山寨STL实现W记http://www.shnenglu.com/lwch/archive/2012/05/20/175491.htmllwchlwchSun, 20 May 2012 12:08:00 GMThttp://www.shnenglu.com/lwch/archive/2012/05/20/175491.htmlhttp://www.shnenglu.com/lwch/comments/175491.htmlhttp://www.shnenglu.com/lwch/archive/2012/05/20/175491.html#Feedback1http://www.shnenglu.com/lwch/comments/commentRss/175491.htmlhttp://www.shnenglu.com/lwch/services/trackbacks/175491.html
数据l构
- allocator
- 内存?/a>
- 内存池V2
- traits,construct&destruct
- vector
- list
- rbtree
- map&set
- hashtable
- hash_map&hash_set
法

]]>
þƷAVϼ|
97þùۺϾƷŮ
|
2021þþƷ99Ʒ|
Ʒ99Ʒþ|
ۺһ˾þþƷ|
þ99Ʒ鶹|
ŷƷһƷþ|
Ʒ99þѹۿ|
þþƷ˘AV|
ƷþþþþĻ
|
þ¾Ʒ|
þŮƵ|
Ʒþþ|
ŷ龫Ʒþþþ|
ƷžžþƵ|
þþƷһ|
Ʒպҹþ|
þþþavëƬ
|
˾þۺӰԺ|
һaƬþëƬëƬ|
ùƷӰ˾þ|
ƷһþþƷɬ|
þþþAVרJN|
˾þô߽ۺӰԺҳ|
ŮƷþþþá
|
97rþþƷ99|
þþƷ|
Ʒgzþþ|
þþûɫƬ|
þþƷžƷ|
Ʒþþþ|
þۺ³³|
ޡvþþ뾫Ʒ|
þþþþþ99Ʒѹۿ|
þþƷһ
|
ŷպþþƷһ|
þ99ƷСѼ|
97Ʒ˾þþô߽|
þֹۺƷ|
99þ뾫Ʒϵ|
ƷȾþëƬ|