Boolan C++ STL与泛型编程_2

主要内容:

本节主要讲解了面向对象和泛型编程的区别,以及source code所涉及到的基础知识(包括运算符重载、各种模板等),还有利用源码深入剖析了分配器、容器(list, vector, array, forward_list等)、迭代器。

源码之前,了无密码。

1.源码所在目录

  • visual c++源代码文件:..\include 下面有这个c++标准库的头文件(自己的环境vs 2017,C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.10.25017\include在这个目录下)
  • gnc c++4.9.2 头文件的位置:..\4.9.2\include\c++
    stl开头的头文件:\include\c++\bits
    扩充的(分配器)头文件:\include\c++\ext

2. oop(面向对象)与gp(泛型编程)

  • oop企图数据和操作放在一起。而gp是将数据和操作分开,容器存放数据,算法实现各种操作,它们之间通过迭代器联系起来。gp的好处在于容器和算法相互独立,他们之间只要具有可以相互使用的接口即可。eg. ::min() 对于两个类对象比较大小,调用这个算法,就要重载小于号或是自定义比较大小的函数。
  • list不能调用::sort(),因为只有随机访问的迭代器才能调用::sort(),list不支持随机访问。所以list只能用自己的sort.
  • 算法对于元素本身的操作,最终都是比较大小?。

3. 技术基础--运算符重载、模板

四个运算符不能重载: :: , . , .*, :?
类模板、函数模板、成员模板
类模板中又分为泛化、特化、偏特化(局部特化,个数和范围上)

template <typename T> struct __type_traits {...}  // 泛化
template<> struct __type_traits<int> {...}        // 特化

template <class Iterator> struct iterator_traits {...}         // 泛化
template <class T> struct struct iterator_traits<T *> {}       //偏特化,传入指针
template <class T> struct struct iterator_traits<const T *> {} //偏特化,传入常量指针

4. 分配器allocators

  • malloc会在申请分配的内存大小的基础上附加额外的空间,如果申请分配的内存小,那么额外的空间所占的比例就大。
  • 分配器是用来分配内存的,对于VC下的allocator类中有两个重要的函数allocate和deallocate. allocate函数内部会调用::operator new,operator new内部会调用malloc. deallocate内部会调用::operator delete,都没有任何特殊设计。
使用方法:
eg. int *p = allocator<int>().allocate(512, (int *)0);  //首先是创建一个临时allocator对象,之后调用allocate函数分配512字节,且全部初始化为0。
allocator<int>().deallocate(p, 512);  //释放空间,这里要求告知分配的空间大小。
  • 对于BC5下的allocator类,头文件<memory.stl>. 且也是利用::operator new和::operator delete实现allocate和deallocate的,都没有任何特殊设计。
使用方法:
eg. int *p = allocator<int>().allocate(512);  //allocate函数第二个参数用默认值是0.
allocator<int>().deallocate(p, 512);
  • 对于GCC2.9下也是利用::operator new和::operator delete实现allocate和deallocate的,都没有任何特殊设计。GCC2.9在容器中采用的不是allocator,而是alloc, 头文件<stl_alloc.h>. 在扩展空间时,尽量减少malloc次数,做法是创建16条链表,每条链表存放的字节是以8的倍数增长,第0条链表存放8字节,第1条链表存放16字节...。容器会和分配器要内存,容器中元素的大小会被调整为8的倍数,分配器会找到相应的链表上,再调用malloc分配内存,一个链上的每个内存块不带有上下cookie(8字节),只在链的头尾带有cookie,从而大幅节省额外空间开销。
  • GCC4.9则没有在沿用GCC2.9这种好的方式,而是使用的是std::allocator, 继承于new_allocator, 且又是采用和vc和bc一样的分配和回收方法。GCC4.9中alloc还存在,只是名字改为了__pool_alloc. eg.vector<string, __gnu_cxx::__pool_alloc<string>>vec;

5. 容器之间实现关系

对于GCC2.9,属于早期的标准库版本,容器之间是复合的关系,而不是继承的关系。heap中拥有vector,priority-queue中拥有heap, stack和queue都拥有deque. 关联容器set、map、multiset、multimap都拥有rb_tree(高度平衡的二叉树).

6. 深度探索list

  • 容器中要提供迭代器,和一系列操作符重载。迭代器内部要定义迭代器相关的5种typedef。
  • ++运算符的重载:前++没有参数,后++是有一个函数参数的,为了与前++进行区分。后++首先会用tmp记录原有的指针,再对原有的指针自加,最后返回tmp。
self operator++(int)
{
     self tmp = *this; //会调用iterator的拷贝构造函数是对list中的node进行拷贝,并不是调用operator*。
     ++*this;
     return tmp;  //这里返回的是对象。而前++返回的是reference。
}

对于整数变量,前++可以加两次,后++则不行。迭代器的自加也是按照这个原则设计的。

  • GCC4.9相对于GCC2.9在list容器上的改进:1. 其中的_list_iterator类模板的typename进行了精简,由3个typename改为1个typename. 2. _list_node在2.9中是由data和两个void_pointer类型指针组成,而在4.9中则是先构造_list_node_base这个结构存放两个指向自己这种类型的指针,之后_list_node继承于它,并存放data,这样指针指向的类型就确定了。3. 在32位cpu下,4.9中sizeof(list<int>) = 8,有两个指针,分别指向prev和next节点; 2.9中是4,只有一个指向node的指针。
  • list最后一个有数据的节点的下一个节点是空的,end()指向的就是这个节点。这样是为了前闭后开遍历设计。

7. iterator需要遵循的原则

  • 迭代器内部定义的5种迭代器相关的typedef是为了给算法调用的。
    eg. rotate()算法的内部调用iterator_traits<_Iter>::iterator_category (); 萃取出iterator_category(迭代器的分类,如前向迭代器,只能++,或是双向迭代器等),还萃取出difference_type(两个iterator之间的距离)和value_type(容器中元素类型)。和迭代器相关的还有两种typedef,分别是reference和pointer从没有在标准库中使用过。
  • iterator_traits(迭代器的萃取机)有一个泛化的类模板,如果iterator是class,那么会使用这个泛化的类模板,还有两个偏特化类模板,一个模板参数特化为T ,另一个特化为const T。所以无论iterator是类还是指针,都可以萃取其中的5种类型,具体使用eg. iterator_traits<T>::value_type。value_type主要是用于声明变量的,所以即使是const T*类型,value_type也是T。
  • 标准库中还有其他的萃取机。

8. 深入探索vector

  • 各个编译器下,容量都会2倍增长。vector中有三个iterator(start指向第一个元素的位置,finish指向当前存放的最后一个元素的下一个位置,end_of_storage指向可以容纳最后一个元素的位置),vector的函数begin(),end(),size(),capactity(),empty(),front(),back(),operator[]等都是利用这三个iterator计算的。
  • push_back()首先会利用finish和end_of_storage指针判断是否还有空闲位置,如果有,则插入元素,finish自加;如果没有,那么会调用辅助函数insert_aux(end(), x),这是一个函数模板,其内部会再次判断是否还有剩余空间,因为其他函数也可以调用这个函数,如果没有剩余空间,那么会分配原有空间大小的二倍,(如果原有空间是0,那么新的空间大小会是1),之后将原有的元素拷贝到新的空间,再将新的元素放进去,finish自加,之后会在新元素后面(安插点后)将剩余的原有元素拷贝过来(这里也是因为这个函数会被其他函数调用)。
  • 32位下sizeof(vector<T>) = 12(有3个指针)。

9. array和forward_list 单向链表

  • TR1版(是1998-2011中间的过度版本,大概是2003左右)
    array内部是用数组实现的,大小不能扩充,所以定义时要声明大小,eg. array<int, 10> arr; 如果给定的array的大小是0,那么在array内部会变为1. 这里的iterator是指针。
  • G4.9 array内部声明数组变得更加复杂了。
    typedef int T[100]; T c; 这样写是对的。

10.forward_list

  • 与list类似,只是是单向的。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容