c++02 - STL标准库和泛型编程笔记
1. 认识headers、版本、重要资源
c++标准库大于stl,stl占标准库70%,stl由6大部件构成,编译器已经带着标准库,标准库开发团队把所写的标准库的东西放到一个namespace里,叫std,即standard,例如vector全名是std::vector,using namespace std表示把std这个命名空间全部打开,后面再使用标准库的东西就不用加上std::,或者写using std::cout,以后写cout就代表std::cout
不同的编译器,开发平台,所带的标准库的用法写法是一样的,
2. STL体系结构基础介绍
容器把内存的问题解决了,程序员看不到内存,分配器是来支持容器的,帮助分配内存,容器是一个模板类,generic programming模板编程的思想,
第二个模板参数是分配器可以不写,在容器的源代码里用默认的分配器,有默认值,分配器本身也是一个模板,必须告诉它每次分配的是什么东西,此处每次分配的内存是一个int,要搭配,vi是object 对象,迭代器是泛化的指针,
count_if,count是计算,if指带一个条件,所以此算法能帮助计算给它的条件之下符合这个条件的元素有几个,
predicate谓语,判断一个东西是真或假,传回真或假,此处条件为大于等于40,
begin指向第一个元素,end指向最后一个元素的下一个位置,即前闭后开,
auto elem,elem的类型其实是iterator,本来auto要写成container::iterator,所以elem就会取出后面vec里面每一个元素出来,auto加引用取出来的每个元素都是引用,修改后会影响原来的值,
3. 容器之分类与各种测试

容器分两种:序列式容器,sequence containers,关联式容器,associative containers,关联式容器的元素有key和value,所以这种容器适合做大量快速的查找,
c++11新出现的容器unordered container,不定序,其实底部是用哈市table做的容器,
c和c++本来就有数组,是语言的一部分,而在c++11把array也变成一个class,红框的都是c++11增加的东西,array是把语言的数组包装成一个class,
序列式容器:
array无法在前后扩充,
vector后面可以自动扩充,分配器负责,
deque双向队列,前后都可以扩充,念dek,
标准库提供的list是双向链表,实际是双向环状链表,
c++11新增了forward list单向链表,
关联式容器:
set 集合,map,内部是用红黑树做的,红黑树是高度平衡二分树,左右差不多平衡,在标准库里并没有规定set或map应该用什么来实现,但是由于红黑树很好,所以编译器所带的标准库都用红黑树做set和map,map的每一个节点有两个东西,一个是key,一个是value,而set的key就是value,value就是key,set要求所放的元素不能重复,map是元素的key不能重复,但是multiset或者multimap表示key可以重复,
hash table 的最好的做法是separate chaining,冲突的用链表连起来,每一个就是一个篮子bucket,每个篮子就是连一个链表,
array.data()会传回这个数组在内存里的起点的地址,
大部分容器都有push_back函数,从尾部放入元素,vector只能往后扩展,vector是两倍增长,size是真正元素的个数,capacity是空间有多大,一个string字符串大小是一个指针的大小,data函数得到整个vector的起始地址,
find是一个模板函数,但是在用模板函数的时候和一般函数用法一样,前面加::表示是全局的函数,所有的算法都是全局的模板函数,find返回一个iterator,
全局有个sort,某些容器也有自己的sort,此时用自己的sort比较快,
forward_list单向链表有push_front只允许放在头部,
slist是gcc有,和forward_list单向链表一样,引入头文件是放在ext目录文件夹里的,extension扩充,其余用法概念一样,
一段是一个buffer,一个buffer可以放很多个元素,此处一个段落放了8个元素,deque就是由一段一段构成的, 分段连续,但是让使用者感觉整个都是连续的,五个指针指向五个buffer,每个buffer的元素都有次序性,deque的每次扩充是一个buffer,即一段,deque没有自己的sort,要用全局的sort,list和forwardlist有自己的sort,用自己的sort,
stack和queue这两种容器的源代码其实就是拥有一个deque,因为deque功能最强大,能够支持写出堆和栈,由于这两种容器里面其实没有自己的数据结构,是借用deque,所以从技术上很多人不把这两个叫做容器,而叫做容器的adapter,即容器适配器,
stack是先进后出,所以不会提供函数可以得到一个iterator泛化的指针,如果提供这种函数,就有可能用这个iterator去改变某个元素内容,或者删除插入,会破坏容器的独特性质先进后出,所以没有find,queue也一样,
multiset里面是红黑树所形成的一个底层结构,这个容器没有push_back也没有push_front,安插元素用insert,这个元素不是安插到头或尾,元素落在树里的位置有一定的规则,自己有find函数,比全局的find快,
mutimap的*pitem是那个pair,
unordered_multiset不是用红黑树s支持,而是用hash table,是一种separate chaining分裂的串,以前叫hash_multiset,也用insert插入元素, bucket_count是篮子的数量,bucket_size(i)第i个篮子挂了几个元素,篮子的数量一定比元素数量多,因为长度不能太长,load_factor装载因子,max_load_factor固定为1,
map放入元素可以用c[i]=string[buf],会把i和buf组成一个pair,
4 分配器之测试
不同标准库下分配器不同,gnu和vc不同,
分配器就是一个class,提供两个函数,一个是allocate,一个是deallocate,不应该直接用分配器,因为还要记住当初要了多少个单元,allocate(1)的1就是一个元素,此处元素是声明allocator里面的int,要还,所以应该去使用容器。如果有小量的容器需求就用new搭配delete或malloc搭配free,不要直接用分配器,
5 OOP (面向对象编程) vs. GP (泛型编程)

容器和算法sort两者的关系要借助iterator,当要对vector和deque容器排序的时候,调用::sort这个全局函数,并且把要操作的范围告诉它,
list无法像vector和deque一样使用标准库的全局的sort算法,标准库的sort所用到的迭代器要有一定的条件,而这个条件是链表list提供的迭代器不能满足的,因为需要随机访问迭代器,而list链表是一个一个节点用指针串起来,并不是一个连续空间,所以它所具备的迭代器是不能跳来跳去的,只能往前进一个,或者后退一个,不能随意加几个,
如果有一些容器自己带着sort,就必须用里面自己的sort去排,如果没有带才用全局的sort去排,
6 技术基础:操作符重载and模板(泛化, 全特化, 偏特化)

,type被绑定为int,而type被抽出来了,所以template里就是空的,是特化的形式,
黄色的部分是一个typedef,会被转换成template<>,即看到template<>,就表示要特化,此图的泛化下面有七个特化版本,即hash这个类模板当接收特别的类型的时候,有独特的设计,本来最上面是空的
一个类模板是allocator,可以接收任意的_Tp,但是如果这个_Tp是void,就有独特的特化的设计,特化又成为full specialization全特化,完整的特化,
有两个模板参数,现在变成一个了,把第一个模板参数绑定为bool,即vector本来可以接收任意类型作为元素,但如果指定那个元素是bool值0和1真假值,那么标准库就说有个特别的设计,效率比较高,此处的局部是数量上是局部,是局部特化,即偏特化,还有一种是范围的局部特化,偏特化,比如之前接收任意类型,现在接收pointer to T,泛化的如果是一个pointer,就有独特的设计,const T*是指针指向const T,
7 分配器

operator new 函数会调用malloc,malloc所给的大小比要的还多了很多的东西,要的越大,附加的东西的比例占的越小,要的越小,附加的东西占比越大,附加的东西可以叫overhead开销,
allocator这个class一定是个模板,每一个分配器最重要的两个函数一个是allocate一个是deallocate,allocate函数会调用Allocate函数,而Allocate函数又会调用函数operator new,operator new函数又会调用malloc函数,所以在vc提供的分配器,分配内存的时候调用的是malloc,会得到所需的内存大小以及一些overhead额外的开销,回收内存的时候调用deallocate,会调用operator delete,operator delete函数会调用free函数,
allocate本来是类模板,加上参数int后变成allocator类名称,直接加小括号,一个typename后面直接加小括号形成一个object,临时对象,还的时候还要记住之前是多少个,不方便,但是容器使用没有问题,
所以vc的分配器没有任何独特设计,就是调用c的malloc和free分配和释放内存,且接口设计不方便使用,容器使用没有问题,
vc bc gcc三者一样,他们的分配器最终就是使用malloc和free分配和释放内存,因此会带来大量的额外开销,通常区块小,额外开销大,所以gcc不用,gcc用的另外的分配器是alloc,
所有容器在gcc下使用的分配器是alloc,额外开销里,上下两个cookie,记录整块的大小,因为malloc拿内存,malloc拿到一个指针,还的时候只还这个指针,不需要告诉大小,大小记录在cookie里,所以free函数能找到cookie,就知道回收多大的内存,由于malloc给各式各样的需求使用,有大有小,所以需要cookie记录,但是容器的元素的大小一样,所以可以不需要把每个元素的大小都记录,所以alloc分配器尽量减少malloc的次数,不要cookie,设计了16条链表,某一条链表负责的某个特定大小的区块用链表串起来,第0条链表负责的是8个字节的大小,第7条链表负责的是78=56个字节的大小,第15条负责的每一个区块是168=128字节,所有的容器需要内存的时候向分配器要内存,容器的元素大小会被调整到8的倍数,比如50调整到56,此时分配器就会看是某个链表负责,里面有没有挂内存块,如果没有再向操作系统要即malloc,要一大块,然后切割为小块,给出一块,切出来的用单向链表串起来,所以切出来的每一块都不带cookie,
cookie在很多c++平台上都是上下都有,因此消耗8个字节,所以选择一个容器,元素放100万个,由于这100万个元素都不带cookie ,所以可以节省800万字节,

gcc4.9分配器又改为vc,bc 的样子,之前gcc2.9的还存在,但是不是std的命名空间里的,是放在gnu_cxx的命名空间
8 容器之间的实现关系与分类

rb_tree红黑树 redblack,复合关系就是拥有即set里面有一个红黑树,map里面有一个红黑树,在做底部的支持,管理各个元素,
heap是一种特殊的容器,里面有一个vector,priority_queue里面有一个vector,stack里有一个deque,queue里有一个deque,
有一个class A和class B,如果A要用到B里面的功能function,可以让A继承B,也可以让A拥有一个B,都可以让A用到B
一个list在gcc2.9下大小是4,这个大小与容器里有多少元素不同,大小指一个容器控制整个数据结构需要几个指针的本身,
8.1 深度探索容器list

整个list类class内的data只有node,node 的类型是link_type,link_type是一个指针,指向list_node,所以在32位电脑上一个指针node大小为4字节,所以sizeof取list对象的大小就是4字节,一个list本身即node是一个指针,指向的list_node是一个节点,
iterator是一个smart pointer,聪明的指针,必须是个class,所有的容器除了vector和array之外,所有的容器的iterator都必须是一个class,才能设计出聪明的动作,才能成为一个智能指针,所有的容器为了供应给使用者iterator,所有的容器都要有一个typrdef,名字叫iterator,必然代表某一个class,
iterator里大量的操作符重载,iterator要模拟指针,几乎所有的容器的iterator都要做5个typedef,泡泡就是iterator,里面有一个指针node,++i是前置型,i++是后置型,由于++i和i++,唯一的参数就是i本身,所以只有一个参数,无法区分是i++还是++i,c++规定为了区分,前++没有参数,i以及变成调用这个函数的object本身,为了区分,后++有一个参数,但是这个参数无意义,只是形式上加上int,
前++就是取出node里的next指针取出并设给泡泡里node这个指针,即指向后一个元素,前–就是取出node里的prev指针并设给泡泡里node这个指针,即指向前一个元素,
后++是把原来的东西记起来,然后进行操作,再把原来的值传回去,c++不允许连续两次后++,所以iterator的后++为了阻止连续两次++,就传回的不是reference,前++传回reference,模拟整数的两次++,
*node取得整个节点,
4.9版不再是pointer to void,而是指向自己,链表的指针指向自己,避免了转型,
最后一个元素的下一个元素不属于这个容器,这个元素要是灰色的,所有的容器都会提供begin和end这两个函数,begin传回来第一个元素,end传回来最后一个元素的下一个元素,
list在4.9版大小为8,因为list本身没有data,所以大小为 0,但是有父类listbase,所以大小是父类的大小,父类多大他就多大,父类的data是m_node,类型是listnodebase,大小为两个pointer,大小为8,即蓝色部分
9 迭代器的设计原则和Iterator Traits的作用与设计

设计一个traits就是希望能对东西进行萃取出想要的特征,算法在操作的时候想知道iterator有哪些性质,可能会选择一个最佳化动作,
category分类,指iterator的移动性质,有的iterator只能++,有的可以跳着走,difference距离表示同一个容器里面两个iterator的距离通常用ptrdiff_t这个type表示,value_type即iterator所指的元素本身是什么type,第四种是reference,第五种是pointer,后两者从来没使用过,这五种type是iterator迭代器的associated types,和这个迭代器相关的五种类型,迭代器本身一定要定义出来,回答算法的提问,
链表元素是T,则value_type就是T,bidirectional是双向,
iterator迭代器是泛化的指针,普通指针也是迭代器,只不过是退化的iterator,指针无法回答算法问题,所以需要萃取机,迭代器泛化的指针可以回答算法问题,
iterator_traits收到一个东西I,算法想要知道I的五种相关类型,算法就来问traits,traits就转问I,traits就是中间层,
10 vector深度探索

没有任何一个东西可以原地扩充,因为要了一块内存后,这一块内存后面的内存可能后续会被使用,所以如果扩充,要找另外一块空间去扩充,
只要靠三个指针就可以控制整个容器vector,所以sizeof(vector)为3*4=12字节,
所有的容器只要是带着连续空间的特性就必须提供中括号[],deque也有中括号,
元素的拷贝会引发拷贝构造函数,每一次扩容会大量调用元素的拷贝构造函数和析构函数,
2.9版本中,pointer to value_type,而value_type是T,所以vector的iterator就是一个指针指向T,T是元素类型,当算法问iterator里五个相关的类型的时候,通过traits去问,吧iterator丢给traits,所以当这个迭代器iterator即指针丢给traits萃取机就会走入对指针的偏特化版本,对于一个指针,特化版本是图右边中间的部分,
新版的vector没有data,但是有父类,父类的大小就是vector的大小,父类的大小为M_impl,大小为三个指针12字节,allocator作为父类没有大小,
11 array,forward_list深度搜索

c++1.0是1998,c++2.0就是2011年,tr1是2003年,技术报告1中间版本,array必须指定大小,不可扩充,传入的int会赋值给value_type,而m_instance是一个数组,当接收的nm数量为0时,会变成1,
只要是连续的空间,迭代器就可以用单纯的指针来表示,不需要另外设计class,iterator是pointer to value_type,value_type就是_Tp,即直接把指针拿来当迭代器,
萃取机会跑到指针的偏特化的版本,
12 deque,queue和stack深度搜索

念dek代课,
deque扩充通过分段串接,一段叫做一个buffer缓冲区,图中有一个vector,里面的每一个元素只是一个指针,每个指针分别指向各个缓冲区buffer,所以这五个缓冲区的次序要看vector里的五个的次序,
最后pushback放元素在尾端扩充,只要再分配一个缓冲区buffer即一段再串接到vector的格子上就可以扩充,前面pushfront同理,
deque的迭代器是一个class,这个class内部有4个元素,其中的node指的就是控制中心vector,所以迭代器知道控制中心在哪里,迭代器++的时候就知道去哪个分段,
deque对外显示分段连续,连续是假象,分段是事实,而迭代器在走的时候为了维持连续的假象,当迭代器走到了尾部或头部的边界,都能够跳到下一个缓冲区buffer,所以迭代器要能回到控制中心来,所以一个buffer的边界靠first和last指针指出,回到控制中心靠node指针指出,cur就是迭代器所指的缓冲区里的那个元素,
所有的容器都维护两个迭代器,分别指向头和尾start和finish,所有的容器都提供两个成员函数,begin传回start,end传回finish,
map这个变量是指针指向指针,T**,因为map指针所指的控制中心里面每一个元素都是指针,size_type的大小是4,在deque里,一个iterator大小为16字节,所以一个deque大小是40字节,创建deque的时候,这个对象本身40字节,
后++调用前++,后–调用前–,借由++和–,让使用者感受不到分段,感受到连续,deque可以连续移动多个位置利用+=和-=,判断是否要跨越缓冲区,知道要跨越多少缓冲区后,迭代器就会先退回控制中心,然后到应到的缓冲区,再决定剩下走多少,-=可以用+=表现,
queue和stack内含一个queue,并且封锁某些功能,queue内数据为c,类型是sequence,是模板参数,默认内部有一个deque,动作都转调用c去做,有时说queue是adapter而不是容器,
stack也一样,同样原理,内含一个deque,不给stack和queue提供迭代器,
13 RB-tree 深度搜索
关联式容器用hashtable或红黑树,红黑树是平衡二分搜寻树
begin函数记录着最左边的节点,end函数记录着最右边的节点
第二个模板参数是key和data一起构成value,第三个模板参数要求必须告诉红黑树在这个value里,在这个整个节点里怎么拿出key,第四个告诉红黑树怎么对key比大小,
红黑树的data,第一个是node_count节点元素的个数,类型是size_type,类似于unsigned integer,head本身是一个指针,
在标准库里,函数可能是仿函数function object或functor,想象是一个函数,实际上是一个class类所创建的对象,仿函数大小为0,因为行为像数据,没有data member,而不管是任意的编译器,对于大小为0的class,创造出来的对象永远是1的大小,所以红黑树的大小是4+4+1=9,9向上调为4的倍数就是12,
在双向链表里有一个为了前闭后开放上去的虚空节点,不是真正的元素,红黑树也有一个东西刻意放上去的为了实现方便,就是header,
第一个模板参数放key的type,第二个模板参数放value,两个东西的组合,此处刻意的让key是integer,value也是integer,即没有data的部分,这个节点的值就当作key,没有其他的部分,第三个模板参数告诉红黑树怎么从value取出key,此处key就是value,value就是key,所以设计一个函数,此处,使用标准库提供的函数identity同一个东西,对小括号进行重载, 所以identity这个class所做出来的object对象的行为就像function,所以叫仿函数或者函数对象,此仿函数大小为0,没有任何的data,identity传一个东西给他,就传回来这个东西,所以元素是多少,经过identity后传回来就是多少,就是key,第四个模板参数,告诉红黑树两个key如何比大小,选择了标准库提供的less这个仿函数,如果传x和y给less,就用小于符号比,
面向对象,会实现成在一个class里,会有一个指针,来具体表现它的实现的手法implement, 这种设计叫handle and body,set和map里都有一个红黑树,红黑树在旧版12字节,新版24字节,
14 set,multiset深度探索

rep_type就是红黑树,set由于底部是一个红黑树,所以有一定的排列次序,不可以拿迭代器去改元素内容,当set拿迭代器时,拿到的是红黑树的const_iterator,这种迭代器不允许改迭代器所指向的内容,
set的所有操作都转调用底层的t即红黑树操作,所以set是一个container adapter容器适配器,set从value取出key用identity仿函数取出,视为相同,
vc6将identity写为内部的class kfn,功能一样,接收一个x就传回一个x,
15 map,multimap深度搜索

map有一个变量t,类型是rep_type,rep_type的类型是ru_tree<模板参数>,即红黑树,里面是红黑树的模板参数,第三个模板参数是map告诉红黑树如何从value中取出key,此处红黑树第二个参数是pair<const int,string> 所以select1st<value_type>就是从value里面拿第一个即key,
map的迭代器可以改data,不能改key,map的迭代器就是红黑树的迭代器,map自动把key设为const,在pair里,所以key放进去后无法被改,因为是const,而data可以被改,set不允许修改元素是借由迭代器是const,而map不允许通过迭代器改key是因为把key变成了const key,select1st本身重载了小括号,是仿函数,函数对象,传给他一个pair,则将pair里的first传回来,
lower_bound如果找的key不存在,就会返回最适合安插这个key的迭代器,multimap不能用中括号插入元素,map可以用中括号插入元素,中括号是先用lower_bound再用insert,所以比insert慢,
15 hashtable深度探索

有一堆东西要放在一个容器里,每一个东西折射为数值或者号码,这个号码的变化如果从0到2的32次方减一,则需要元素的大小乘以2的32次方的空间去放,
bucket就是篮子筒子,经验:如果元素个数比篮子个数更多,就危险,就要打散,即rehashing就会把篮子增加为两倍,这些元素落在哪个篮子上就会重新计算,素数作为篮子数,53的两倍106,附近的素数是97,所以篮子会从53扩充到97,打散以后这些链表就会变短,对于安插查找元素就会变快,
hashfcn用于告诉它每个元素通过这个function得到编号,hashfcn是仿函数或函数对象,hashfcn算出来的编号叫做hashcode,就是用hashcode除以篮子的个数,得到的余数就是这个对象东西放的位置,第四个模板参数extractkey就是萃取出key,因为放入散列表的是一包东西,即怎么从一包东西可能是个pair一对东西中取出来key,第五个模板参数叫equalkey即告诉hashtable什么是key相等,
一个hashtable的大小,数据中前三个就是模板参数里的hashfcn,extractkey,equalkey,这三个type所做出来的object,因为模板参数传进来的是三个type,传进来的三个type用typedef重新换了一个名字,根据type创建对象,这三个可以说是函数对象functionobject,
buckets就是一堆篮子即一个vector
最后一个变量记录共有几个元素,
hash,equals get_key是function object,大小为0,实际上是1,buckets本身是vector,vector里有三个指针,所以buckets12字节,size _type是unsigned int 4字节,所以1+1+1+12+4=19,19调整为4的倍数20,因为要对齐,所以每一个hashtable 大小是20字节,
value就是key,key就是value,所以所以从value里取出key用identity,告诉hashtable两个字符串什么时候相等,而用的c风格的字符串,用指针指着头,不能比两个指针,要比较所指的内容是否相等,所以写出一个仿函数告诉hashtable比较,此处不把strcmp写到eqstr处,即函数名称strcmp就是函数地址,因为接口不一致,hashtable要的eqstr函数要传回Boolean值true或false,不能传-1代表false,1代表true,
使用hashtable最困难的事情就是决定元素怎么转换成一个编号,即hashfcn,选用红黑树或者其他的容器,只要指定元素类型就可以放,但是当选用hashtable,或者是由hashtable所做成的set或者map的时候必须写一个hashfcn,
hash function传出来的就是hash code,模板的偏特化,对小括号操作符重载,如果hash指明里面是这些类型,这些东西就会成为仿函数,函数对象function object,因为重载了小括号,如果面对数值本身,hashfunction就会把数值当作它的编号,
如果是字符串,就会将字符的ASCII码进行计算,制造出一个尽量不重复,比较乱的数字,
标准库没有对c++的字符串提供hashfcn,对c的字符串提供了,
modulus是除了之后得到余数,
16 unordered 容器
标准库的篮子的数量一定比元素多,否则会rehashing,且篮子的数量标准库已经写好了,
17 迭代器的分类

迭代器由容器提供,array是连续空间,移动的特性就像指针一样,是random access 随机访问,可以跳,vector也是连续空间,也是random access ,deque是分段连续假的连续,但是给使用者感觉是真的连续,所以它的迭代器也是random access可以跳,list是双向链表,不能跳,不连续空间,所以迭代器是bidirectional双向,往前或往后,forward list只能走一个方向,是单向链表,是forward iterator的type,红黑树的节点是双向,所以红黑树做出来的set map multiset multimap这些东西全部都是双向bidirectional iterator,unordered 不定序的东西底部都是hashtable,有好多篮子,每个篮子下面是一个链表,这个篮子下面是一个单向或双向决定出hashtable是单向的还是双向的迭代器,标准库用五种class表示迭代器的分类,
array<int,10>::iterator是一个type类型,后面加空的小括号是临时对象,临时对象就是没有对象名称的,各自容器的迭代器都做了临时对象,都传到这个display_category函数,传进函数的是各种类型,所以display_category函数写成模板,传进来的itr不知道是什么类型,是I,编译器会推导出来, typename iterator_traits::iterator_category cagy;这行代码中,拿到的是迭代器iterator, 就会丢到萃取机traits中,iterator_traits::iterator_category 整个就是提问,在问萃取机交给你的迭代器的category是什么,如果编译通过,iterator_category就是答案,而这个回答太长了,所以换一个名字叫cagy,再调用另一个函数_display_category(cagy),上面五个函数名称相同,是函数的重载overloading,这五个函数收到的参数的类型不同,参数就是迭代器的类型,所以对于分类要用类的对象表示
istream_iterator和ostream_iterator都不是来自容器,这两个迭代器是适配器adapter
把放进来的参数iterator放到typeid里,typeid是c++提供的一个操作符,typeid(itr)得到一个对象,通过这个对象可以调用name函数,
istream_iterator有两个模板参数,后面的版本有四个模板参数,但是第三和第四个有默认值,所以仍保持接口一样,新版和旧版都在定义自己的分类iterator_category是input_iterator_tag
父类只是一些typedef没有data没有function,子类继承父类相当于拥有了五个typedef,
18 迭代器分类(category)对算法的影响

distance算法就是要获得两个指针的距离,distance会调用_distance函数,n是计数器,不是随机访问迭代器就走①,循环走判断距离,是随机访问迭代器就直接last减去first,
两根指针的距离用什么type表现,迭代器的五个类型中有一个是difference_type距离,所以此处要问萃取机放进来的迭代器的difference_type是什么,
iterator_traits::difference_type这一行如果编译通过,答案就会放在inline后,这一行就是标准库里distance的返回类型,iterator_traits::iterator_category是问萃取机放入的迭代器的category是什么,这一行是问也是回答,编译后就是回答,通过typedef将这一行换个名字为category,然后调用上面两个版本的函数,category()是typename加小括号创建一个临时对象,编译器会根据第三参数调用上面的某个函数,
distance是主函数,根据里面的分类category调用不同的次函数_distance,
分类用对象来表现,因为有继承关系,如果traits问出来的结果分类 category是forward iterator,代码里面并没有一个对forward iterator设计的版本,图中1和2是input iterator和random access iterator的两个版本,现在如果问的是forward iterator,面对对象对于继承的概念是是一种 is a,假如问出来的结果是forward iterator,虽然没有forward iterator的专属版本,但是forward iterator is a input iterator,即forward iterator是一种input iterator,所以会转到1的函数,所以虽然有四种,但是只有两个版本的函数,但是一定会落在某一个版本,
advance算法,把收到的迭代器走n个距离,第三个参数是问迭代器的分类是什么,然后才能决定上面三个_advance 的函数选哪个,
distance函数是直接用trait萃取机去问,此处advance函数用了辅助函数,iterator_category,也是把收到的迭代器的类型丢给traits去问,
copy是从来源端到目的端,所以copy这个函数有头和尾两个指针指向来源端,目的端只需要有一个起点就可以了,所以应该有三个参数,result就是目的端的起点,copy先检查传进来的泛化的指针即迭代器,first,last,result,检查他们是不是const char这种类型,如果是,copy就调用底部低阶memmove,c的函数,来源端直接搬到目的端,如果检查出来是wchar_t类型,也做memmove,如果是inpuriterator,就调用copy_dispatch,在这个函数里检查传进来的是不是指针,trivial表示不重要的,不重要的就不调用拷贝赋值,重要的就每一个赋值都要调用拷贝赋值,
iterator traits,萃取机,把迭代器放进去,问一些问题,type traits也是把类型放进去,问一些问题比如你的拷贝赋值重不重要,比如复数类没有指针,数据就是实部和虚部,这样的类不需要写析构函数,拷贝构造,拷贝赋值函数都不需要写,此时用编译器默认给的,什么都没有做,此时把复数丢到type traits去问就能问出来它的拷贝赋值是不重要的,
如果摧毁的元素对象的析构函数是不重要的,destroy就不用做什么,如果重要就要去调用析构函数,
__unique_copy,这个函数有两个下划线,说明一定不是主函数,是另外一个主函数判断出迭代器是forward iterator,然后调用这个版本__unique_copy,
传进来的result是目的端,if(*result !=*first)dereference是读取内容,如果传进来的是outputiterator,如果没有对outputiterator写一个版本,就会到forwarditerator版本,这个版本里面却是read,因为result是目的端,一个outputiterator不可以做read的动作,所以就写了result是outputiterator的版本,
算法里对于迭代器的分类没有强制限制,只有暗示,因为算法是一个模板函数,模板可以接收各式各样的类型,针对迭代器的分类用此函数去做,但是主函数没有强制只接收什么样的迭代器,sort没有在接口要求传给他的必须是random access iterator这种迭代器,所以sort接收头尾两个指针,是任意类型,是模板参数,把迭代器的类型T命名为randomaccessiterator,为了暗示sort接收randomaccessiterator,
19 算法源代码剖析(11个例子)

qsort和bsearch这两个不是c++的标准库的算法,是c的function,c++的算法前面带着两个指针用这样的接口与容器沟通,
accumulate累计,很多算法都有两个版本,此算法版本1接收头尾两个指针,以及一个初值,版本2接收头尾两个指针,以及一个初值,以及一个两个操作数的运算,
其中减法minus()是functor仿函数,是c++标准库提供的,myfunc和myobj都是自己写的functor,传进去的东西只要被小括号作用起来,类似binary_op(init,*first)的小括号,小括号叫做function call operator函数调用操作符,小括号作用在binary_op的上面,传任何东西进去比如myfunc,myobj能被小括号作用就可以,把binary_op这种叫做callable entity可被调用的东西,一般的函数当然是可被调用的东西,如myfunc,所以可以放进来作为accumulate参数,或者写一个function object函数对象,所谓函数对象就是右边struct myclass 到myobj的部分,是一个类或者是一个结构体创建出来的对象,这个类有重载小括号,因此传入accumulate内后左边init=binary_op()的这个小括号就相当于在调用自己写的function object的重载的小括号,
即可以写一个一般的函数,或者写一个像函数的对象object,所以这种类做出来的对象的行为像函数,称为function object或 叫functor仿函数,仿是模仿,类似,
foreach就是在一段区间,范围内对每一个元素做一件指定的事情,for_each接收first,last以及一个函数动作f,只要f能被小括号调用起来即可,
range-based for statement,冒号左边是一个变量声明,冒号右边是一堆东西,可以放一个容器,
replace取代,此函数接收头尾两个迭代器,以及旧值和新值,元素如果和旧值相同就替换为新值,算法名称后面带if就说明要给这个算法一个条件,replace_if的参数都是模板参数,所以这些参数的类型叫什么都是任意,第三个模板参数是predicate判断式,会传回真或假,此处pred(*first)表示如果元素值符合这个判断式,即传回来是真或假只有两种情况,
左边是标准库的算法,全局性的在std命名空间里,右边是这些容器里有没有和标准库这个算法同名的成员函数,count_if判断是否有符合pred条件的,如果有就加一,右边的八个容器本身带有count,即当在使用这八个容器的时候如果要用count,就调用自己的成员函数,不要用左边泛化的函数,自己的成员函数快,
这八个容器就是关联式容器associated container,是可以用一个key去找到一个data,是一个小型的数据库,内部是用红黑树和散列表做出来的,所以这八个用count就有自己比较快速的做法,同理,find这八个也有自己比较快的做法,有自己严谨的结构,而sort这八个没有sort,因为这八个在遍历元素加加的时候本身就形成了sort,不要再拿里面的元素进行sort了,放进去就是有序的,
array的名称就是array 的头,把这个数组的头尾当成容器vector的初值,vector有一个构造函数是接收这样两个参数,myfunc里面return (i<j)表示由小到大排,
myobj重载了小括号操作符, 是一个functor,图中三个sort都是由小到大,sort传入rbegin和rend,作为头尾,r代表reverse反向逆向,逆向的begin和end,
只有单向链表和双向链表自带sort function,所以对这两个容器排序的时候用自己的成员函数,不要用左边的sort,因为算法sort要求random access iterator是可以跳的迭代器,但是这两个容器都不可以跳,

容器不一定是连续的,调用begin时候,得到的迭代器,前闭后开区间,后面的指针是最后一个元素的下一个位置,所以不可以对最后的迭代器取值dereference,上面的图左右颠倒后变成中间的图,方向相反了,上中合为一个图就是下面的图,rbegin就是逆向的begin,
必须先排序,再用binary_search,是让lower_bound来做二分搜寻,lower_bound找到的是在不违反排序的状态下,安插进去的数的最低点,upper_bound就是安插进去的最高点,
20 仿函数和函数对象
functors仿函数只为算法服务, 仿函数要模仿函数所以仿函数这个class必须重载小括号,小括号是function call operator,
标准库提供了三大分类的functor

function object是对象,less是一个类型type,必须创建一个对象放进去给sort,所以less()产生一个临时对象,
binary_function就是两个操作数,unary_function即一个操作数,这两个类就是接收模板参数,并且换了名字,没有data,没有function,大小为0,实际为1,但是作为父类有其他类继承了它,那么此父类的大小就是0,
如果写的functor可以被修改,适配,就要选择一个适当的来继承,因为被adapter改造的时候adapter有可能来问这些typedef的问题,很想算法问迭代器问题,adapter问问他,functor回答问题,
仿函数就是一个class里面重载了小括号,这样的class创建出来的对象叫做函数对象,或仿函数,因为做出来的是一个对象但是像一个函数,
21 适配器adapter
23个设计模式中,有一个就是adapter,adapter就是把一个既有的东西改造一下,比如改动几口,把参数数量改一下,或者把函数的名称改一下,
根据adapter要改造的或者修饰的是什么而命名,改变functor所以叫functor adapter,关键在于adapter要改造一个东西,A把B改造后,A就代表B了,使用者看到的就是A,但是A把事情交给B去做,因为B是本来已经有的功能比较好的东西,所以A是使用者和B的桥梁,所以这就是adapter的作用,A要去取用B的功能,有两种做法:A内含B即复合,或者A继承B,都可以让A调用B,B是幕后被调用的,A是改造后的,使用者可以使用的,
下列的adapter都是拥有,复合,比如functor adapter内含了一个functor,iterator adapter内含了一个iterator,container adapter内含了一个container,
算法不知道容器,能够看到的只是迭代器,迭代器有容器所提供,所以算法想知道的一切问题都要问迭代器,就会通过五个typedef去问,所以迭代器都必须有五个typedef,才能和算法搭配,而adapter作用也是和算法合作,可以把仿函数functor包成一个adapter再传给算法,因此adapter也可能想要知道内含的仿函数functor的一些特性,也会提问,需要三个或者两个typedef来回答,
stack和queue都内含一个sequence,sequence默认是一个deque,即默认内含一个容器,内含后要改造,内含的东西有很多函数,放进来后只开放几个,也算改造,内含的容器有一个函数是push_back,现在改名为push,或者pop_front改名为front,这也是一种改造,即使用者调用front就是调用内含的对象里的pop_front,即把内含的deque改成stack和queue的风貌,
一个函数被调用的时候才指定参数,真正调用是把这个条件传入,count_if看看每一个元素有没有复合这个条件的,此处less()没有调用,less()是对象,类型加小括号是对象,把第二个参数记下来40,bind2nd是一个辅助函数,
有一个class比较大复杂,是主题,另外有一个小的辅助函数,帮助使用,所以此处binder2nd是主体class,而bind2nd是辅助函数,
less()将会被记录在op,40会被记录在value里,这两个是这个类里的data,而bind2nd这个adapter去修饰functor less()之后,要表现的也是function,如果修饰容器,之后要表现的也是容器,如果修饰迭代器也要表现迭代器的样子,所以binder2nd要重载小括号这个操作,所以bind2nd修饰好less(),40整体之后交给count_if,count_if会逐一的找到元素执行给的条件pred,而所给的条件就是bind2nd(less(),40)这一串,执行到if(pred(first))的时候,就会调用右边的重载的小括号的函数,才把记起来的比大小的动作less()记在op里,把op和value这两个拿出来用,才真正的去调用这个op即less(),并且把value当成第二参数,而x就是左边每一个元素逐一的传进来first,即把原来修饰的东西以及参数记下来,也没有绑定,然后到后面被调用的时候才把记起来的东西使用,此时才有绑定第二参数
由于binder2nd是一个类模板,所以如果要使用的话要指定模板参数是什么,此处的模板参数是一个operation,即less这个类型,因为less()是一个对象,所以此时用外界的接口bind2nd,此时less()和40是传到bind2nd的参数op和x,然后bind2nd函数帮忙创建binder2nd这个类的对象,此处,binder2nd(op,arg2_type(x))是typename加小括号,就是产生一个临时对象,而创建对象要指定模板参数,此处借用了函数模板可以做实参推导,所以less()放进bind2nd的op后,到底是什么type,编译器就会自动推出来,就会把推出来的operation参数拿来做下面的binder2nd(op,arg2_type(x))里的模板参数,此即辅助函数的价值,创建binder2nd类的对象即binder2nd(op,arg2_type(x))并且把op和x放进去,就会调用起binder2nd类的构造函数,构造函数其中有两个参数x和y,构造函数就会把x记录在op里,把y记录在value里,就把这个对象丢给count_if,count_if也记录下来,这是count_if的第三参数,pred就是整个bind2nd(less(),40))这个东西, pred就是对象,if(pred(first))就会调用小括号函数调用操作符,然后就把右边存起来的op和value拿起来用,此处x是左边的first
less传入的是int,所以第二参数40也应该是int,要能检查出来,而检查方式是靠typedef,主体和辅助函数都是adapter,图左上的问operation即问op参数less()的类型Operation你的第二实参second_argument_type是什么类型,此处换了名字为arg2_type,换了名字后看看能不能把x转换为arg2_type这种类型,即问40是不是问出来的second_argument_type这一种类型或者可以转换,如果不能转换,此处就会报错,所以operation必须回答这个问题,右下图的的调用小括号传回的类型也要是之前的类型,A修饰B,A得到的结果的type就是B之前被调用传回的type,B原先传回什么,A也要传回这种类型,所以adapter被调用的时候,应该传回的type应该问adapter修饰的Operation,binder修饰好之后,binder本身又变成一个function要被调用,被调用应该传回binder这个functor所修饰的东西即less()所传回的东西,即bind2nd修饰的那个Operation即less(),问Operation你的result_type是什么,另外,重载小括号被调用的时候,count_if调用,每一个元素传进来将会变成比大小的第一实参,第二实参已经被绑定为40了,传进来的唯一实参是x,x是什么type也要问,binder2nd问Operation你的第一实参是什么,x必须是这种type,
由于bind2nd是要修饰less(),less是比较两个操作数的动作,所以可能出现的提问和回答,你的第一个实参x是什么type,第二个实参y是什么type,比完后的type是什么,所以一个函数对象或者函数必须能够回答这三个问题,才能够和adapter如bind2nd搭配,如果能够回答就叫这个functor为adaptable可适配的,functor的可适配adaptable条件是less继承了binary_function,binary_function有三个typedef,即first_argument_type,second_argument_type,result_type如果function继承了binary_function,就会拥有这三个typedef,就能回答了,
这些很长的东西前面都会加typename,编译器都带一套标准库,有点编译器不加typename,这个typename是要帮助编译器通过代码,因为编译器在编译的时候,这个东西还没有使用,所以Operation是什么还不知道,此处的typename就是告诉编译器不要犹豫,Operation::second_argument_type就是一个typename,让编译器通过,
bind2nd修饰好一个less的functor后,本身又形成了一个functor,这个functor如果还要再被adapter,就也要回答那些问题,所以它binder2nd又继承了unary_function,less函数本来又两个参数x,y,绑定一个后只剩下一个参数了,所以bind2nd对外界只有一个参数,回答两个问题,第一个问题是实参是什么,第二个问题是结果是什么,本身去代表别人,那么本身的身份就是被代表的那种人的身份,
比如比大小接收的是T这个模板参数,传进来传到左边是T,T,bool,如现在要比的是整数int,右边就会变成int,int,bool传到左边Arg1,Arg2,Result就是int,int,bool,则first_argument_type,第一实参是int,second_argument_type,第二实参是int,result_type结果值是bool值,
binder2nd以及binder1st已经过时了,它出现了functional头文件里,要用左边的bind来取代,bind现在也放在functional里,
not1有一个辅助函数在图左上角,有一个主class在右边,not1辅助函数所接收的pred参数就是整个bind2nd(less(),40),传进来就会进行实参推导,因为是一个函数模板,推导出predicate是什么type,推出来就会创建右边的unary_negate,左边的unary_negate就是typename,typename加小括号就是创建临时对象,调用起构造函数,所以会调用右边的constructor,左边的pred就传到右边的构造函数里的x,x就记起来,记在右边的pred里,之后上面的not1(bind2nd(less(),40))一整个放入count_if里,count_if获得第一第二第三参数,相当于左上角的not1辅助函数创建出右下角的类的对象,传入左下角的pred第三参数里变成实参,而左下角的count_if调用for循环,面对每一个元素,都调用传进来的pred即not1(bind2nd(less(),40)),调用小括号就会到右下的小括号重载,被调用的时候,pred(x)才终于把记起来的东西拿出来用,记起来的东西就是bind2nd(less(),40),即小于40这个动作,并且加上一个否定!,所以变成不小于40,即大于等于40,
22 bind新型适配器c++11

c++11提供的adapter 是bind绑定,bind可以绑定一般的函数,也可以绑定函数对象,
_1 表示占位符号,由于要使用占位符,所以要写using namespace std::placeholders同时要找到对应的头文件,这样代码里就能看到_1,_2,_3,绑定的时候用占位符就能保留这个参数,在调用的时候就要给定这个参数,
bind(my_divide,_1,2)此时出现的占位符placeholder,要用一个东西占位符号来表现第一参数,实际调用的第一参数就会占到_1这个位置, 占位符就会使占位的地方空着参数,
bind加上模板参数就是指定return type返回类型为int,而不是之前的绑定哪个函数,返回类型就是绑定的东西的返回类型,bind只能指定一个模板参数
MyPair ten_two {10,2}是c++11的新写法,在一个对象后面加上大括号即给初值,
bind(&MyPair::multiply,_1)成员函数有一个参数this,所以占位符占了this的位置,调用换了名字后的成员函数时,_1指this pointer,所以要把东西this传入,此处传入ten_two,这两行就相当于在调用ten_two这个object,通过这个object去调用刚刚绑定好的乘法multiply,multiply的返回类型是double,所以20是20.00,
_1,_2都是没有绑定,如果直接写出值才是绑定参数,_1表示留着,所以调用的时候要指定参数,bind(&MyPair::a,ten_two)此处不留着,已经绑定了,所以下一行调用换名称后的时候就不用指定参数了,bind(&MyPair::b,_1)绑定这个class里的b,_1表示不打算绑定这个object,object要放进去,绑定好了后换了名称调用的时候因为没有绑定,所以要给定object,就会获得b,
c++11写法,一个容器后面加大括号{}直接给值,
cbegin表示传回的迭代器是const不可以改的内容,后面的是不小于50
bind第一实参保留,第二实参绑定为50,
23 迭代器的适配器

adapter来改造iterator,
在排序的时候默认情况是由小到大,但是如果传给它的起点和终点是逆向的,rbegin和rend,意思是仍然是由小到大排,但是是反方向排,从尾巴往头排,所以从头到尾看就是由大到小,所以由大到小排有两种做法,第一种是给他比大小的准则,第二种是默认准则,但是排的次序头尾相反,r就是reverse,取得逆向的头,即正向的尾部,逆向的尾部,即正向的头,为了实现这个,迭代器要做一个adapt动作,这个adapter叫做reverse adapter,因为要修饰iterator,所以内部一定要准备一个iterator,
逆向迭代器取值的时候是把正向迭代器退一格取值,
adapter就是把索要修饰的东西记起来,然后再看要如何去改造
插入的动作是insert动作,而不是assign动作,要新安排空间,而不是赋值,copy的assign已经写死,所以要把赋值=改为insert,采用inserter把目的端的迭代器改成是一种insert的动作,此处的inserter是辅助函数,inserter接收两个参数,一个是容器,一个是迭代器,要把接收的x和i套到adapter即insert_iterator里的container和iter接收这两个值,关键在于adapter里进行赋值操作符的重载,result是copy的第三参数,即调用端的第三参数inserter(foo,it),如果result对赋值重新定义,那么这个赋值操作符就会作用在它身上,就会使得adapter即insert_iterator接管赋值操作,赋值的时候走重载赋值的部分,就会调用这个容器的insert,就会多一个元素的空间,并且迭代器后移,
24 未知x的适配器(没听 听不懂)
cout是一个对象object,所以可以取地址,out_stream是一个指针指向cout,所以*out_stream就是cout,
25 一个万用的hash function

customer当成元素放在容器里,所以要为这个类写一个hash function,hash function有两种形式,一种是单纯如右图写为一般的函数,另一种是设计为成员函数,即在一个类里面,这个类的对象要成为一个函数对象,要能够像函数一样被调用,所以要重载小括号操作符,
左边这种选择一个hashtable所形成的容器,并且指定元素类型是Customer,hash function是CustomerHash,而hash function可以当函数用,当以函数的形式用的时候调用里面的重载小括号,
右边搭配容器,第一个模板参数仍然是元素类型,第二个模板参数是函数的类型,必须把一般的函数的类型写出来,写完后在创建容器的时候还要把真正的函数的地址放进来,上面只是类型,下面要指定到底是哪一个函数,左边的不用,只要指定类型就好,里面会自动产生customerHash的对象,这个对象将会当成一个函数对象被调用,
基本的类型都有hashfunction,针对基本类型都有特化版本的hashfunction,std::hashstd::string是一个类型,后面加小括号是产生一个临时对象,这个对象被当成函数来用所以再加小括号,传字符串给它,把一个复杂结构拆分为基本类型后把他们各自的hashcode相加,但是碰撞会多,查找会慢,
tr12003年版本给了另一种做法,属于c++2.0即2011年的部分,
c++2.0新提供的语法,template<typename…Types>可以接受任意个数的模板参数,variable templates可变化的模板,hash_val①和②实际参数②的第一参数是size_t,①的不是,③的第一个参数是size_t,第二个是const T,
customerhash的hash_val函数调用①,①就准备一个种子seed,然后调用上面②版本,②先把种子做一个combine动作,种子变了后,再调用参数是种子和三个实参,调用到②自己,进来之后在参数的地方,三个实参被分为一个val和两个,而val这个分出来的一个参数又拿来变化种子进行combine操作,之后再调用一次自己,
即本来传5个东西进来,加上一个种子,五个里面取出第一个出来去变化自己,变化后五个就剩四个,四个就变成一加三,每次总是把n个分为一个和其余,再把其余分为一个和更少的其余,直到两个变成一加一,只剩一个元素,就会调用③版本,variable templates可变化的模板拆解不定量的参数,做到了递归,seed是传递引用,所以改动就会影响原来的种子,
combine,改变的方式是做hash_combine运算,并且把一个一个传进来的val都调用val自己本身这种基本类型的hashfunction,并且加上十六进制的一个数黄金比例,再加两个,所以hash_val函数里的三个参数一个一个的被分解开来,每一个拿出来算出hashcode再进行运算,最终获得的最后的一个种子就是当作最开始的三个参数形成的一个customer当成它的hashcode,
篮子个数永远大于元素个数,之前写死的素数新版没有了,customerhash这个hashfunction创建的对象是函数对象,可以当函数用,调用小括号,放入七个元素,hh就代表hashfunction酸算出对象的hashcode,除以11的余数是看看落在哪个篮子上,因为现在有11个篮子,
在新版里,所有基本类型都有自己的hashfunction了,string也有,
26 tuple 用例

tuple可以放任意的类型以及个数,数之组合
string的大小是一个指针的大小,
get<0>(t1)表示要拿出t1的第0个元素出来,可以用make_tuple辅助函数直接把值放进去,编译器会做成一个tuple,编译器会对类型使用实参推推导,tie()=t3把t3的值赋值绑定到tie里面的参数上,
tuple_size::value是问的方式,把turpletype放里面,问tuple有几个成分part,或member,tuple_element<1,tupletype>::type是提问,编译后通过是回答,tuple_element<1,tupletype>::type也是结果,把结果换了名字为T,之后可以声明一个对象或者变量,问里面的第一个第二个都是什么类型,
head和tail都是任意类型,variable templates可以把一个类变成继承关系,variadic templates会自动的递归,所以会自动的继承尾部的部分,而头部用于声明一个变量,
子类的大小是自己的data加上父类的大小,所以最下面的子类的大小是一个整数加上上面,上面的大小是一个float再加上上面,上面的大小是string加上上面,上面是0,所以整个tuple的大小就是整数加float加string,
tail传回自己,转型为inherited,尾部形成的部分,指针位置没变,但是指的范围变了,
27 type traits

默认构造函数,拷贝构造,拷贝赋值,析构函数是否不重要要回答这些问题,默认的回答都是false,所以都重要,c++标准库为基本类型写了特化的版本,都是不重要的,编译器的默认版本够用了,不用写拷贝构造和析构函数,
算法会问foo通过typr_traits的destructor是不是不重要,得到真或假,来设计快速的算法,设计类的人设计这个类的type_traits,
现在设计类,直接提问就可以得到答案,is_void::value就会把T放入is_void这个traits,有一个typedef叫value,即回答真假值,就是1或0,
一个类带指针基本要写虚构函数,不带指针基本不用写,使用的string其实是一个typedef,其实是调用basic_string(char)第一个参数是ASCII的字符char,而basic_string有析构函数,一个类准备把自己当作基类父类的时候才需要写virtual destructor,如果不会当成父类,就不用写虚析构函数,字符串不会当成父类,所以basic_string有析构函数,但是不是虚析构函数,所以has_virtual_destructor是0表示没有,
polymorphic是多态,
左边第一行Zoo(int i1,int i2):d1(i1),d2(i2){}是构造函数,下一行是拷贝构造,但是会delete,表示写出来不要,因为不写delete编译器会给默认的,所以写明不要拷贝构造,再下一行是移动构造,搬移构造,赋值default是要编译器给一个默认的版本,再下面是拷贝赋值,是默认的,再下一行是搬移赋值,delete表示不要,is_copy_constructible是可以拷贝构造吗即有拷贝构造函数吗,左边是delete,所以右边是0表示没有,
新版不用配合写偏特化,直接问type trait就可以得知各种特点,
收到_Tp后先丢到里面,remove_cv,c就是const,v就是volatile,先把这两个关键字拿掉,左边最上面是泛化,下面是偏特化带着const的_Tp,
传一个类型给is_integral,is_integral会先把const和volatile拿掉,然后丢给helper,如果符合偏特化,回答就是真,除此之外就是false,意思就是不是整数型,
28 cout

cout是一个对象object,类型是_IO_ostream_withassign,extern关键字表示这个变量cout可以被外界使用,这个文件外都可以看到cout,而,_IO_ostream_withassign继承ostream,而ostream虚继承ios,里面都有操作符重载,
除此以外的类型要自己重载<<,比如basic_string,复数,线程,正则表达式sub_match,bitset所以可以把这些对象放入cout,
29 movable元素对于deque速度效能的影响

放入容器的元素类型加不加moveable功能会影响效率,即有无moveavle功能的元素,
类里有静态的数据定义,所以要在类外有静态的初值定义,
深拷贝:不但把指针拷贝过来,因为这个对象本身只有指针,还把指针所指的东西也拷贝,
&&是reference of reference,指针拷贝过来,然后要把原来的指针打断,move只是拷贝指针,move之后原来的东西不可以被再使用了,
更多推荐
所有评论(0)