STL 侯捷 (一)

STL第一讲

由于这一讲解比较零碎,我记录的也比较的零碎:

标准库和标准模板模板的概念,前者包含了后者,前者还包括了一些零碎的函数库。

这一讲主要讲解了我们STL之间的关系:

STL常见的6大部件就是容器,适配器,分配器,算法,迭代器,仿函数;

解释一下我们其中关系,我们的容器内存管理是由我们的分配器完成,并不需要我们自己进行内存的管理,而其实我们可以跨过容器直接对分配器进行操作,但是其实没有这种必要的,因为我们在利用分配器分配器进行分配操作之后,释放内存需要我们呢记住分配好的内存大小,我们对比一下malloc和free函数free需要我们记住malloc的大小吗?所以这种直接使用不利用内存的管理。

而我们的算法给了我们一些操作容器的手段,那么这些手段操作都需要落实在我们容器上面的每一个元素上面,这就是那么就有了我们呢迭代器的产生。而适配器的作用是将我们输入的东西,通过包装来以便能够顺利的被我们的容器所使用。

首先明白我们有时候在全局有一个算法函数,一般借助::调用,而在容器自己内部也有一个算法函数,一般用用法为st.find(),优先使用我们的自己所带的函数。

deque是一个双向队列,其内部实现的结构如下所所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8G6EtKUN-1587476515253)(C:\Users\梁锐\Desktop\学习笔记\STL\STL第一讲.assets\image-20200421170016121.png)]

其也是我们stack和queue这两种容器的实现方法,deque包含了其所有的操作方法,而且由于这两种容器特殊的结构,所以不会提供迭代器来进行内部元素的访问,以免产生对结构的破坏;

那么接下来聊一聊我们的hashtable,在hash进行篮子分配的时候,我们的篮子数量一定会大于元素的数量,而当元素的数量到达一定的程度的时候,我们就寻找双倍的篮子大小,那以前篮子的值重新打碎放进篮子里面;还有就是明白 map可以使用[]重载操作符,而mutimap就不可以使用[]重载操作符;

第二讲

OOP:将data和methods放在一起

GP:将data和methods分开来。这样容器和算法的团队就可以各自操作,期间用迭代器来进行联通就可以了。

​ 那么就可以解释一下为什么List为什么不可以使用算法里面sort而是要自己内部写一个sort,就是因为算法团队中使用了迭代器的+5,-4这种连续跳跃操作,而这种操作只用关联容器才可以使用,所以list就没有办法利用迭代器使用算法sort,而是自己在容器里面写了一个。

理解一下什么是泛化什么是特化,和偏特化;特化标志(tempalte<>);

举个例子:

template <class type>
    struct __type_traits{
        typedef __true_type  this_dummy_member_must_be_first;
        typedef __false_type has_trivial_default_constructor;
    }
-----上面就是模板
template<> struct __type_traits<int>{
    typedef __true_type  has_trivial_default_constructor;
    typedef __ture_type  has_trivial_copy_constructor;
}
------上面就是特化 特化成为一个int的版本
  特化有什么好处?编译器及时可以根据具体的类型去做更好更精确的事情,比如大家调用这个函数都是输出自己的类型,而我就是想给int搞特殊,让他输出“hello world”;

偏特化其实就是局部特化,并不是所有的参数都是特化;偏特化还可以分为数量的偏特化和范围的偏特化;比如传入的参数个数和参数的类型

泛化
template <class T,class Alloc=alloc>
 class vector{
     ...
 };
特化:
template <class Alloc=alloc>
 class vector<bool,Alloc>{
     ...
 };
那么以上就是在我们调用vector的时候,在调用bool容器的时候进行一些特殊的操作,但是分配器还是不变的,可以随意传入一种分配器类型;
类型导致的偏特化:
template <class Iterator>
    struct iterator_traits{
        typedef typename iterator::iterator_category iterator_category;
        typedef typename iterator::value_type  value_type;
    }
下面就是偏特化的版本,只要我们传入的一种参数的指针,那么就进行下面的一种惭怍,而不是上面的那种
template <class T>
    struct iterator_traits<T*>{
        typedef random_access_iterator_tag iterator_category;
        typedef T   value_type;
    }

理解了上面的一些部分我们开始进行源代码剖析:


分配器allocators:头文件

我们分析一下分配器的源代码会发现,其实allocator是调用了operator new的函数,而继续追根朔源,就会发现其实operator new函数内部就是调用了malloc。那么我们呢来研究一下malloc函数,

当我们调用了malloc之后,会发现返回的空间比我们申请的空间还要多,其中包括了字节对齐和头部尾部记录分配大小所占用的空间。

而当我们调用了deallocate的时候,调用了operator delete()—>free来释放空间,其实并没有任何其它特殊的设计。但是以上分配器都是很烂的,因为其实当我们利用容器分配内存的的时候都是很小的,比如一个int,然后malloc返回的时候,会导致很多额外空间浪费。比如光说100个int,就会浪费800个字节的头部尾部记录空间大小。-------所以说以上的分配器都很烂----

然而在2.9版本alloc 一种特殊的分配器,其可以很好的解决这个问题:我们思考一下容器是否需要进行分配空间大小的记录吗,我们容器里面的类型都是一致的,所以alloc就以这种一大块的方式来进行解决,当我们呢寻求一块大小的时候,我们直接挂在我们的编号下面对应的大小就可以了。

然而到了4.9版本,alloc函数改成了__pool_alloc函数,alloc分配器取消了。这么好的分配器怎么就取消了呢?

迭代器的设计原则和ioterator trails;

trials就是认为设计的一种萃取机

我们知道,迭代器作为我们算法和容器之间的桥梁,算法需要冲迭代器中获取相应的信息;这就是为什么我们呢迭代器设计中一定有五个固定的typdef:
在这里插入图片描述

为啥说迭代器是一种泛化的指针?就是因为迭代器在指针的基础上,能够在容器之间完成指针相应的功能,这就要求迭代器能够“智能”,如果list的迭代器++不是只是的单纯的完成指针意义上的++,而是List节点内部找到next并进行指针转移。此外迭代器并且能够提供其他信息以供给算法使用;

那么算法是怎样获得这些信息呢?

其实过程就是算法通过向迭代器进行提问,迭代器完成回答,但是有时候我们的迭代器退化成了指针,指针没有的我们之前所说的那五个typedef,那么算法怎样完成提问呢?也很简单,这时候用一个计算解决问题的常用思想,给其加一个中间层,就是间接提问,这时候的我们萃取机也就出场了:
在这里插入图片描述
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-weoln3fQ-1589597511304)(C:\Users\梁锐\Desktop\学习笔记\STL\STL第一讲.assets\image-20200516104019773.png)]

萃取机通过我们偏特化的效果,来进行实际的区分我们指针和迭代器:

​ 这里的迭代器萃取机只是萃取机中的其中一种,以后还会遇见更多;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值