C++ 标准库与泛型编程
该课程也可以叫C++ 标准库——体系结构与内核分析;课程精髓是从源代码分析C++STL之体系结构,而STL正是泛型编程最成功的作品,所以也是以STL为标准深层次的探讨泛型编程。
STL六大部件
六大部件关系 容器结构与分类 复杂度 前闭后开区间迭代器像是泛化的指针;
迭代器串联起算法和容器;
adapter可以转换容器、迭代器和仿函数
范围访问 auto关键字 容器--结构与分类 测试程序辅助函数头部为第一个元素,尾部是最后一个元素位置的下一个位置
序列式容器
容器array容器vector1和c语言的数组一样,大小固定,之后不能改,size大小是常量才可以
容器vector2 容器list两倍增长 扩充,会有很多内存碎片
容器forward_list 容器slist优先使用容器自带的迭代器,原因是:标准库的sort中需要使用到随机访问迭代器,而list这类数据结构无法提供随机访问能力,所以内置一个sort不使用随机访问迭代器进行排序
容器deque1所属gnu c++ 非标准c++库 单向链表
容器deque2 容器stack 容器queue可以向前或向后生长,分段连续
每次扩充一个buffer,buffer多大看后续?
关联式容器
容器multiset容器multimap底层数据结构是红黑树
容器unordered_multiset1 容器unordered_multiset2 容器unordered_multimap 容器set 容器map 容器unordered_set1底层数据结构是红黑树
容器unordered_set2 容器unordered_maphashtable做支撑,bucket后面是一个链表(可以是单向也可以是双向),bucket一定比元素数多,如果bucket的数量与元素数量相等,那么就会扩充bucket的数量为两倍,然后重新hash
分配器
使用分配器1 使用分配器2 使用分配器3基础 OOP && GP 1 OOP && GP 2 OOP && GP 3 OOP && GP 4 操作符重载11代表一个元素,直接使用分配器的时候,需要自己申请和释放,不建议直接使用 分配器
操作符重载2 操作符重载3 类模板 函数模板 成员模板 特化1 特化2 特化3 偏特化4运算符重载的标准是模拟整数的操作
分配器1 分配器2 分配器3类模板可以 局部特化,分为两种数量的偏特化和范围的偏特化,如上图,左边是数量的偏特化,右边是范围的偏特化
分配器4 分配器5 分配器6 分配器7 分配器8 分配器9 分配器10 分配器11 迭代器 容器-结构与分类1 容器-结构与分类2allocator<int>() 是一个临时对象
容器和迭代器 容器list list 迭代器1 list 迭代器2这里的意思是set中包含一个RB_tree;
stack是基于deque实现的
heap是基于vector实现的
priority_queue也是基于vector实现的
list 迭代器3 list 迭代器4 容器list 迭代器遵循的原则LIST ++运算符的重载
注意:copy构造与这里的*运算符重载
运算符重载的标准是模拟整数的操作
迭代器提供的5种关系iterator_category是指迭代器的类别,(1,只能前进 ++; 2,只能后退--; 3,可以跳跃+=3 +=4;)
value_type是指迭代器所指向元素本身的类型 (string int)
difference_type 指两个迭代器距离 需要用什么类型来表现,比如 vector的difference_type 是uint32_t
trits特性指针也是一种迭代器,是一种退化的迭代器
iterator_traits既然指针也是一种迭代器,所以如果传进来的类型是指针,那么就无法分辨是普通的迭代器,还是指针类型,所以使用萃取机来中转判断一下,先判断传进来的迭代器是指针类型还是普通的迭代器
完整的iterator_traits 各式各样的traits 容器vector1 容器vector2萃取机使用偏特化的方式实现,偏特化出指针类型的iterator_traits,即可
容器vector3insert_aux()可能被其他函数调用所以需要再次检查空间
2.9 vector iterator有可能要被insert()调用,所以前后的元素都要拷贝到新的空间
4.9 vector 4.9 vector iterator1走偏特化的版本,直接萃取出类型
4.9 vector iterator2有一堆复杂的继承关系,好在大小还是12字节
array1 array2 forward_list deque1走泛化版本的萃取机,获取类型,实现相比4.9变得更为复杂,最终呈现的结果一样
deque2 deque iterator deque insert1 deque insert2 deque模拟连续空间1 deque模拟连续空间2 deque模拟连续空间3 deque模拟连续空间4 deque模拟连续空间5 4.9版deque1 4.9版deque2 容器queue 容器stack queuq&stack底层结构1deque双向开口,vector单向开口
queuq&stack底层结构2stack queue都不允许遍历,也不提供迭代器
都可选择list 和deque作为底层数据结构 deque更快,性能好
queuq&stack底层结构3关于能选那个容器作为底层数据结构,只要调用的时候底层数据结构都有对应的实现就可以调用
stack可以选择vector作为底层数据结构
queue不可选择vector作为底层结构,但是也并非完全不可以
容器rb_tree1 容器rb_tree2不可选择map或者set作为底层数据结构
容器rb_tree34+4+1=9 对齐之后是12字节,1是空类的大小(compare functor)
容器rb_tree 用例1 容器rb_tree 用例2 容器rb_tree 用例3 容器rb_tree 类图identity是gnu c++编译器独有,less是编译器都有
unary_function和binary_function是adapter
容器set & multiset 容器set4.9新版的所有的容器实现中,严格遵守了面向对象oo的设计思想,handle-body的实现模式(桥接模式),对外的类只有接口,具体的实现隐藏在impl指针指向的类中
vc6 容器setset可以当作container adapter stack和queue也可以被称作adapter 因为不做事,把事情全部交给底层的数据结构
使用容器multiset 容器map & multimap 容器mapvc6没有identity,但是自己实现了一个inner class kfn的东西和identity差不多
vc6 容器map 使用容器multimap 容器map【】这里可以清楚的看到value=key+data 这里用pair对key+data进行了组装
使用容器map 容器hashtable1 容器hashtable2[]会先找到map中当前元素的第一个位置,或者最适和插入该元素的位置,所以这里性能很不错,有优化,
不过insert更加快一点,因为[]最后插入的时候也是调用的insert
容器hashtable3bucket数量一般为质数,gnu-C中起始值为53
当元素个数和bucket数量一样是,bucket数量翻倍
bucket数量扩充是扩充为多大是写死的,存在一个static数组中
容器hashtable4 hash-function & hash-code 1 hash-function & hash-code 2 modelus运算 hash-function & hash-code 2 2.9 hashtable用例 4.9 hashtable用例 hash容器改名为unordered容器 容器 unordered_set1 容器 unordered_set2 c++ 标准库算法简介 iterator_category1hashtable迭代器中有两个指针 一个指向链表中的元素,一个指向bucket,用来支持遍历操作
模板函数可以和普通函数重名?这种不算特化,叫重载?
调用函数时会进行严格的类型匹配,但是模板函数不会进行自动类型转换,而普通函数具有隐式的类型转换。
其调用顺序应该是:
- 首先寻找参数完全匹配的普通函数,如果找到了就调用它
- 若1不存在则寻找一个函数模板,将其实例化,产生一个匹配的模板函数,若找到了,就调用它
- 若1,2都皆不存在,会寻找低一级的对函数的重载方法,例如通过类型转换可产生参数匹配等,若找到了,就调用它
- 若1,2,3均未找到匹配的函数,则是一个错误的调用
参考
iterator_category of istream_iterator获取c++语言本身给类型的名字 typeid().name ,得到的是编译器给这个类型的名字
三个版本接口一样
iterator_category of ostream_iterator iterator_category 影响算法1这里有一个神奇继承关系,itertor是一个只有内嵌类型(typedef)的类,没有function,没有data,ostream_iterator继承之后,也只是为了继承iterator中的内嵌类型
iterator_category 影响算法2根据迭代器的类型,选择不同的计算迭代器之间距离的方法
iterator_category && type traits 影响算法1根据迭代器的类型,选择 +n 前进 (或者-n 后退)的方法
iterator_category && type traits 影响算法2根据迭代器的类型选择copy的方法,总是会选择最合适的办法去做拷贝动作
type traits .....? 使用type traits判断是否has trival operate=(拷贝赋值到底重不重要) 后面会讲---
iterator_category && type traits 影响算法3根据迭代器的类型选择destroy的方法
iterator_category && type traits 影响算法4根据迭代器的类型选择destroy的方法
算法源码中对iterator_category的暗示output_iterator write only
input_iterator read only
stl有针对传入不同迭代器进行专门的处理
先前示例中出现的算法 算法 accumulate 算法 for_each 算法replace replace_if replace_copy 算法count count_if 算法find find_if 算法sort reverse iterator,rbegin(),rend() 算法binary_search 仿函数functors1 仿函数functors2 仿函数functors3 仿函数 可适配条件因为算法源码都是模板实现,所以理论上不区分迭代器类型,但是有相应的暗示,如上图所示,如果传入的迭代器类型不符合要求,可能会编译失败
stl中多处使用内嵌内型,如下,子类继承父类之后拥有同样的定义
template <class arg, class result>
struct unary_function{
typedef arg argument_type;
typdef result result_type;
};
多种adapter
容器适配器 stack queueadapter都是包含的关系,adapter适配器的意思就是内涵一个东西,然后对这个东西进行改造,如下图
函数适配器 binder2ndstack queue 内含一个deque,然后进行改造,比如push_back()改名为push()
函数适配器 Not1 新型适配器 bind-1多处使用模板编程的技巧,typedef内嵌类型
新型适配器 bind-2所以std::bind是一个适配器 非常有趣
std::bind返回值是function object
迭代器适配器--reverser_iteratorbind用法详解 可以绑定4种东西,返回是function object
迭代器适配器--inserterrbegin() rend()也都是适配器
X适配器--ostream_iterator接管赋值操作,将普通赋值操作变成insert操作
这里有一个神奇的示例,建议测试一下,详细看下代码!!!
for(int i=0; i<10; ++i){
v.push_back(i*10);
}
std::ostream_iterator<int> out_it(std::cout, ",");
std::copy(v.begin(), v.end(), out_it);
X适配器--istream_iterator 1
double v1, v2;
std::cout<<"input two valus: "<<std::endl;
std::istream_iterator<double> eos;
std::istream_iterator<double> iit(std::cin);
if(iit!=eos) v1=*iit;
++iit;
if(iit!=eos) v2=*iit;
std::cout<<"v1: "<<v1<<"v2: "<<v2<<"\n";
X适配器--istream_iterator 2
万用的hash function1istream_iterator构造的时候 已经在等待输入!!!!!!!!!!!!!
万用的hash function2虚线框起来的是上面函数的类型
万用的hash function3 万用的hash function4 万用的hash function5 偏特化的hash function1 偏特化的hash function2 tuple 用例很多技巧:
1 变参模版函数参数,拆分
2 同名函数递归调用
3 pass by reference seed值
4 最后的hash code是seed值
5 seed计算方式采用了黄金比例计算公式,详见下图
tuple源码tuple的一些神奇用法
1 tie
2 tuple_size
3 tuple_element
4 make_tuple
5 get<0>()
type traits1这里同样使用了很多技巧, 关键的技巧就是variadic template 可变参数模板 (自动递归)
1 私有继承
2 一层层剥离模板参数列表
3 tail返回值,this会被强转
type traits22.9版本type_traits 需要自己补充自定义类型的的萃取机制
type traits3 type traits 测试1 type traits 测试2 type traits 测试3 type traits 测试4 type traits 测试5 type traits 测试6 type traits 测试7 type traits 实现is_void type traits 实现is_integral type traits 实现 is_class is_union is_enum is_pod4.9 新版本中定义了一大堆萃取机,不需要补充自定义类型的萃取机制
type traits 实现is_move_assignable 2,9 cout编译器 在其中实现了一些函数,因为编译器知道一个类是否具有拷贝构造,拷贝赋值,是不是一个类型等这些问题
4.9 cout2.9版本中,cout就是ostream对象对操作符的重载“<<”
moveable 元素对vector速度效能的影响同样4.9版本中,cout就是ostream对象对操作符的重载“<<”
moveable 元素对list速度效能的影响 moveable 元素对deque速度效能的影响 moveable 元素对multiset速度效能的影响 moveable 元素对unordered_multiset速度效能的影响 moveable class 实现示例1 moveable class 实现示例2cctor是copy构造 mctor是移动构造 后者性能强很多;
具体差多少跟当前内存情况有关
moveable class 测试函数 vector的copy构造所以移动语意其实是浅拷贝,所以需要注意的是:
使用移动语意之后,原来的object是不可使用的
vector的move构造这个是深copy
string具有移动语意这个是浅copy
以上是string的构造函数,拷贝构造,移动构造