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