浅谈STL

来源:互联网 发布:少儿编程课程 编辑:程序博客网 时间:2024/06/05 19:03

由于我刚开始学习系统的写博客,个人方面不管是能力还是经验都会有所欠缺,所以肯定会有很多不完善的地方,包括结构安排、要点遗漏、知识错误等等,还请各位批判着看,如有问题,恳请斧正,我将十分感激!

STL是一个标准规范,它只是为各种组件定义了一系列统一的访问接口及他们之间搭配使用的一般规则,而没有规定他们的底层实现。因此各开发商都提供自己的STL版本,比较著名的有P.J. Plauger,HP,SGI等。如无明确说明,本文所指的STL均为C++98标准下的SGI版本,下载地址

STL(Stardand Template Library),即标准模板库的简称。于1994年2月年正式成为ANSI/ISO C++的一部分。

C++标准规定,STL的头文件都不用扩展名,所以一般STL的头文件名称是没有.h后缀的。同时,所有的STL组件都被纳入到std命名空间内,所以在include之后要添加下面一行代码:

using namespace std;

STL是一套十分强大的库,基本将我们日常能使用的数据结构和算法封装为模板的形式,这样的好处是该套库有很强的通用性,可以说对任何类型都支持。

而且使用STL的很方便,避免了在编码过程中重复造轮子的工作,使我们将精力集中在业务的实现逻辑上,而不必纠缠一些细枝末节的东西。比如要对一个整形数组iarr进行排序,只需要下面这句代码即可:

#include<algorithm>using namespace std;sort(iarr,iarr+sizeof(iarr)/sizeof(iarr[0]));

这样,我们便可以不用自己编写复杂的排序函数,大大地提高了开发效率。sort函数的原型到后面说算法的部分再说。

STL主要由六大组件组成,他们之间的关系如下:
这里写图片描述

可见,迭代器、存储分配器和函数对象都是为容器服务的。迭代器就像一个指针一样指向容器中某个元素,可以很方便地对其进行存取;存储分配器为容器分配内存空间;

容器

容器类定义在下表所示的头文件中:

头文件 内容(元素类型均为T) list 双向链表 vector 数组 queue 队列 deque 双向队列 stack 栈 set/multiset 集合 map/multimap 映射 bitset 位图 hash_map hash映射 hash_set hash集合

上表中,stack,queue和deque严格来说并不算是容器,而是容器适配器。因为他们是以其他容器来实现的。为了方便,暂且列在这里。

容器按照其存储元素之间的关系又可以分为序列式容器和关联式容器。以上列表中set/multiset,map/multimap属于关联式容器,其他都属于序列式容器。

序列式容器和关联式容器的区别就是,序列式容器里面的元素之间都是有相对的位置关系的,不管是在逻辑上还是在底层实现上他们都有自己的相对位置。因而在序列式容器里面可以使用诸如push_back(),push_front(),pop_back(),pop_front()等函数;而关联式容器里面的元素在逻辑上总是无序的,如集合(集合中元素的性质有:互异性,确定性,无序性)。但为了能在相应的数据结构中存储和附加在容器对象上算法的效率,关联式容器在物理实现上,其元素也是有相对的位置关系的,只不过对STL的使用者而言,这一切都是透明的,不会被其察觉。

list

list的内部被实现为一个双向循环链表,所以在其中进行insert,find等函数的时间复杂度是O(n),但erase函数为O(1)。其节点定义如下:

struct _List_node_base {  _List_node_base* _M_next;  _List_node_base* _M_prev;};template <class _Tp>struct _List_node : public _List_node_base {  _Tp _M_data;};

不得不说,STL真是博大精深。list将节点的前后指针和其包含的元素分离开来了,这样会使list更具有通用性。以上代码中,_List_node_base中的_M_next和_M_prev分别指向节点的后继和前趋节点,将其作为一个struct封装,而后被_List_node继承。

  • list支持的构造方法为
explicit list (const allocator_type& alloc = allocator_type());// (1)explicit list (size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type());//(2)template <class InputIterator>list (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());//(3)list (const list& x);//(4)

下面分别介绍:
(1). 即默认的构造函数,使用list<int> mylist;便会调用该构造函数。当然你也可以把int换成其他的类型。该函数构造一个空的list容器,其中没有任何元素。称为default constructor。
(2). 构造一个含有n个元素的list容器,每个元素都是val值的拷贝。称为fill constructor。即构造出来的list是满的。
(3). 用尽可能多的first和last区间之间的元素构造一个list容器,每个元素对应于元区间的一个元素,并且元素的相对顺序和原来相同。也称为range constructor。
(4). 用另一个list构造该容器,这两个容器中的元素的值,数量及相对顺序都完全相同。因此这是拷贝构造函数(copy constructor)。

  • 析构函数很简单,只有一个:
~list();

该函数析构容器中的所有元素,并释放他们的空间。

  • 重载赋值运算符
list& operator= (const list& x);

作用是拷贝x中的所有元素到当前链表中。需要注意的是,operator=函数不是释放掉当前链表的所有节点后再将x中的元素插入其中的,而是保留了当前空间,直接覆盖其元素值以减少分配空间的开销。

以上就是C++类里的四大金刚。list类支持的其他成员函数还有:

  • 返回迭代器:
函数 功能 begin 返回指向链表头的迭代器(public) end 返回指向链表尾的迭代器(public) rbegin 返回一个反向迭代器,该迭代器指向反向链表的头部(public) rend 返回一个反向迭代器,该迭代器指向反向链表的尾部(public)

* 返回容量:

函数 功能 empty 测试list是否为空(public) size 测试list的大小(public) max_size 返回list的最大容量(public)

* 存取元素:

函数 功能 front 获取第一个元素(public) back 获取最后一个元素(public)

* 修改

函数 功能 assign 修改list容器中的元素(public) push_front 在list首部插入元素(public) pop_front 删除第一个元素(public) push_back 在list尾部插入元素(public) pop_back 删除最后一个元素(public) insert 插入元素(public) erase 删除元素(public) swap 交换两个元素(public) resize 改变list的大小(public) clear 清空该list容器(public)

* 操作方法

函数 功能 splice 在容器间转移元素(public) remove 移除特定的元素(public) remove_if 移除满足条件的元素(public) unique 移除重复的元素(public) merge 合并两个list容器(public) sort 对元素进行排序(public) reverse 反转list容器中的元素(public)

* 分配空间

函数 功能 get_allocator 分配空间

list的内存映像如下:

这里写图片描述

由此可以看出,list只支持顺序存取,而不支持随机存取,因为其底层实现上各元素不是连续存放的。

以上只是对list中的成员函数进行了简单的介绍,其中很多函数都是有重载版本的,在此不再一一列举,具有好奇心的读者可移步:www.cplusplus.com ,然后在最上方的搜索栏搜索”list”即可。其实不止是list,C/C++标准库里面的许多内容这里都介绍得非常全面,本文以下即将介绍的各种东东也可以在上面查到。只不过该网站是英文的,英文和我一样挫的读者就多花点时间读一读,还是很值得的!

本文首发于www.sbrave.cn

【完】

0 0
原创粉丝点击