`
evasiu
  • 浏览: 165239 次
  • 性别: Icon_minigender_2
  • 来自: 广州
博客专栏
Fa47b089-e026-399c-b770-017349f619d5
TCP/IP详解卷一>阅读...
浏览量:12243
社区版块
存档分类
最新评论

C++ STL 学习总结

 
阅读更多

看完《C++ premier》的第二部分,容器和算法,算是对C++中的STL有了一定的了解。总结起来,这里面涉及的主要概念有:容器、迭代器、适配器以及算法。

一、容器

容器容纳特定类型的对象的集合,例如一组整数,一组自定义类SalesItem。有些容器元素按照插入的顺序存放,读取元素时,可以利用存放顺序查找到它,这总容器称为顺序容器;有些容器元素在插入到容器中时,会给一个便于索引的键值,读取元素时,可以通过与元素关联的键值找到它,这种容器称为关联容器。

标准库定义了三种顺序容器,分别为vectordequelist,它们在内存中不同的存储方式导致了他们具有不同的性能优势。Vector存放在一片连续的内存块中,因此它只能向后增长,如果需要在vector的中间插入新元素,则会导致插入位置起后面的所有元素都要往后迁移;另一方面由于它连续存储,因此随机访问元素很方便,支持下标操作和vec.at(n)操作(at比下标操作更安全一些,因为它会检查越界行为)。如果说vector是封装过的数组,那么list就是我们常说的链表,元素与元素之间通过指针相互连在一起,因此如果找准了位置,插入一个新元素(不论在list的什么位置)都只是敲断该位置原先的连接,然后把新元素跟这两段连接起来就好,而不必像vector那样劳师动众。但是,元素可能散落于内存的不同地方,所以元素并不存在所谓的“下标”,读取元素时只能通过遍历寻到。Dequedouble-ended-queue)可以算得上是vectorlist的中和了。它是由一小片一小片连续的内存连接起来的,每片连续的内存存放一定数量的元素,不够用的时候会添加新的内存块,因此deque在前面、后面添加元素都很方便。另外,deque也支持下标操作和at操作,但是效率比vector要慢很多。我们可以从添加、删除元素,访问元素、容器增长收缩这几个方面来比较这三种顺序容器。

1.       添加、删除元素

标准库提供了多种添加元素的方法,如前插入(push_front)、后插入(push_back)以及任意插入(insert)。由vector本身的存储特性决定了vector不提供push_front操作,而dequelist则提供,同时,三种顺序容器都提供push_back操作。Insert操作有三种,它们都必须首先提供插入的位置,表现为一个迭代器,元素将插入到该迭代器指向的位置前,插入的值有三种,一种是一个单值,一种是n个相同的值,最后一种由两个迭代器指定另一容器中的元素的范围。

同样的,标准库还提供了多种删除元素的方法,如前删除(pop_front)、后删除(pop_back)以及任意删除(erase),还有就是清空clear。同样,vector不提供pop_front操作,而dequelist则提供,它们都提供pop_back操作。Erase有两种,一种是删除单个元素,此时必须提供指向该元素的迭代器,一种是删除一段范围内的元素,该范围由两个迭代器表示。

2.       访问元素

Frontback函数分别返回容器的首个元素和末尾元素,list容器有关访问元素的操作就只有这两个了。而dequevector还提供了下标操作和at操作,但是效率有所不同。由于deque是由多个内存块连接起来的,因此访问时必须跨内存访问,因此比vector要慢得多。

3.       容器的增长与收缩

有关容器大小的操作,三种顺序容器都提供了size()来返回容器中元素的数量,以及resize()来增长或减少容器的元素。Resize要求提供resize后的大小n,此时如果n小于原来的size(),超出的部分会被删除,如果n大于原来的size(),则新添加的元素有两种方式初始化:如果使用resize时还提供了另外一个初始值,新添加的元素被赋予该初始值;如果没有,则新添加的元素以该类型的默认值进行初始化。容器随着pushinsert自增长,或者通过resize提供一个更大的值来扩充容量。在扩充容器容量方面,dequevector要高效得多。当deque需要扩充容量时,它只须申请一小块新内存,然后把新申请的块与其他deque块连接起来,vector则必须申请一块比原先内存块更大的内存块,然后把原vector里面的数据复制到新内存块中,再销毁原来的内存块。Vector不支持把容器容量调小,删除元素或resize提供一个小一些的值,vector的内存块并不会自动收缩,相反的,由于deque是由很多小块组成的,当有空闲时,会销毁空闲的内存块,所以deque是自收缩的。Vectordeque多提供了两个操作,capacity()reserve()capacity返回当前vector的容量,而reserve()则通过提供一个值,指定vector应该开辟的内存块的大小。

这三种容器提供的操作总结如下:
 

关联容器有两种,一种是map,一种是setmap中元素是以<key, value>对的方式加入到map中来的,在map中,通过key找到valueset中只有key,每个key只出现一次(map也一样)。关联容器的元素根据键的次序排列,在迭代遍历关联容器时,我们可以确保按键的顺序访问元素,而与元素在容器中的存放位置、插入时间无关。关联容器不提供顺序容器中的push_backpush_frontpop_backpop_frontbackfront操作。

有一种专门的数据类型pair用于表示<key, value>对。可以通过make_pair( t1, t2)直接生成pair,或通过构造函数初始化一个pairpair中有两个公开成员firstsecond,分别表示他的两个成员。

Map中的元素正是pair数据类型,它及其成员在map中有另外一种定义,如下:

 

其提供的插入、删除、查找并读取元素的方法如下:

 

另外值得一提的是map提供的下标操作(通过key索引),如下例子:

 

map<string, int> word_count; word_count["anna"] = 1;


 

 

以上语句 将发生以下事情:

1)在word_count中查找键为"anna"的元素,没有找到(word_count目前是个空map);

2)生成一个新的键-值对,它的键是const string类型的对象,保存anna,而它的值 则采用值初始化,即int的值初始化为0

3)将这个新的键-值对插入到word_count中;

4)读取新插入的元素,并将它的值赋为1

下标操作实际上相当于结合了findinsert的动作,在某些情况下,使用下标操作会使代码显得非常的简洁,但是它有一个副作用:如果该键不在map中,那么下标操作会插入一个具有该键的新元素并自动初始化,有些时候这并非我们所希望的。

set类型只是单纯的键的集合,当我们只想知道一个值是否存在时,用set容器是最适合的。在set中,每个键只能出现一次。它提供map操作总结表中的所有操作。

如果希望map中一个键对应多个值,就如一个作者对应多篇论文那样,则需要使用multimapmultimapmap的操作基本相同,除了查找键对应的元素现在可能变成多个值了,因此有必要考虑如何找出所有与key对应的value值。总共有三种:
策略1:使用findcount操作(假设contactor是一个以人名为键,电话号码为值的map string search_item( "eva siu" ); typedef multimap<string, string>::size_type sz_type; sz_type entries = contactors.count(search_item); multimap<string, string>::iterator iter = contactors.find(search_item); for( sz_type cnt = 0; cnt!=entries; ++cnt, ++iter ) cout<<iter->second<<endl;



 

策略23:使用返回迭代器的关联容器操作

 
也就是说,可以使用lower_bound(k)upper_bound(k)来确定包含健k的范围,或者直接使用equal_range(k)

 

 

 

二、迭代器

迭代器是一种检查容器内元素并遍历元素的数据类型,所有的标准库容器都定义了相应的迭代器类型,例如装了int元素的vector的迭代器iter,声明如下:

vector<int>::iterator iter;

获取容器的迭代器方法如下: vector<int> vec(10); vector<int>::iterator vec_begin = vec.begin(); vector<int>::iterator vec_end = vec.end(); vector<int>::iterator vec_last = vec.rend(); vector<int>::iterator vec_front = vec.rbegin();



 

一般的迭代器都提供自增(iter++, ++iter)、自减(iter--, --iter)、解引用(*iter, iter->mem)、比较相等与否(iter1==iter2, iter1 != iter2)的操作。而vectordeque由于其连续内存的优势,其迭代器还能像下标那样加减一个整数值、加减一个迭代器以及比较大小。

 

 

我们通常使用一对迭代器来标识一个范围,这个范围是一个左闭合区间,即[begin, end)区间。

迭代器常常被用于作为算法(STL的另一个重要部分)的输入,通常用于标识范围或指定位置,但有时候算法需要对容器作出修改(写容器算法),C++中定义了另外三种迭代器:

1.       插入迭代器:这类迭代器与容器绑定在一起,实现在容器中插入元素的功能

2.       iostream迭代器:与IO流绑定在一起,用于迭代遍历所关联的IO

3.       反向迭代器:实现从后向前遍历。

插入迭代器又有三种,分别是front_inserter,要求关联的容器必须提供push_front的操作,该函数创建一个迭代器,调用它所关联的基础容器的push_front成员函数代替赋值操作;back_inserter函数创建一个迭代器,调用它所关联的基础容器的push_back成员函数代替赋值操作;inserter将产生在指定位置实现插入的迭代器。插入迭代器更多的是作为算法的输入参数,例如将算法的计算结果(修改了的容器内容)插入到插入迭代器中。

iostream迭代器分为istream_iteratorostream_iterator,由于它是与流绑定在一起的,因此不存在逆方向,流过去了就没了。istream_iteratorostream_iterator分别有两种声明方式,分别是

 

istream_iterator<T> in_iter(cin); //与某输入流绑定 istream_iterator<T> eof; // istream_iterator对象的超出末端迭代器 ostream_iterator<T> out_iter( cout ); //与某输出流绑定 ostream_iterator<T> out(cout, delim); //输出的时候写时用delim分隔一个个T


 

反向迭代器以容器的最后一个元素为起点,以容器第一个元素的前端元素为终点,自增相当于正常迭代器的自减,其范围可以这样表示[end-1, begin-1)

 

三、适配器

适配器是标准库中通用的概念,包括容器适配器,迭代器适配器,函数适配器。本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制。

容器适配器让一种已存在的容器类型采用另外一种不同的抽象类型的工作方式实现,但是该抽象工作方式的实现依赖于原容器类型所能提供的操作。

顺序容器适配器有stackqueuepriority_queue

Stack是一种后进行出的数据结构,提供的操作只有pop(), push(), top(), empty()等操作,只在栈的顶端进行,因此三种顺序容器的操作均能支持,但它默认基于deque实现。Queue是一种先进先出的数据结构,提供的操作有pop要求能够push_back,又要能够pop_front,因此vector不适合作为queue的基础容器。Priority_queue在插入元素的时候会考虑其优先级,通常优先级由该类型默认的<来体现。

插入迭代器是一种迭代器适配器,它带有一个容器参数,并生成迭代器,用于指定在容器中插入元素。

至于函数适配器,目前还没有遇到过,这里记个空。

 

四、泛型算法

标准容器定义了很少的操作,基本上只涉及添加删除元素、访问元素、获取容器大小、重设容器大小,以及获取指向容器第一个元素后最后一个元素的下一位置的迭代器。但是除了这些操作,我们往往还需要更多的操作,例如查找某个特定的元素,给顺序容器排序,替代具有某种特性的元素的值等等。标准库并没有为每种容器定义实现这些操作的成员函数,而是定义了一组泛型算法:因为它们实现共同的操作,因此称之为算法;而“泛型”指的是它们可以作用在多种容器类型上——不但可以作用于vector或者list这种标准库容器,还可以用在内置数组类型甚至其他类型的序列上,这些算法均在头文件<algorithm>中定义。

算法一般不直接接受某个容器作为输入参数,而是通过迭代器指定容器的某个位置或范围与某个容器关联起来,其中迭代器的范围仍是遵循左闭合区间的约定。

根据算法对容器的使用修改情况,可以将算法分为:

1.       只读算法

只读算法只读取其输入范围内的元素,而不会修改其值,例如像find(查找某一特定元素),count_if(计算符合某一要求的元素的个数)等。此时传递给算法的迭代器可以是const的。

2.       写容器元素算法

写容器算法可能会修改容器中元素的值,如uniqueunique函数将无重复的元素往前复制,从而覆盖相邻的重复元素,最后返回一个迭代器,表示无重复的值范围的结束,这种类型的算法不改变容器的大小,只修改容器中元素的值;又如fill,用一个值替换输入范围内的元素的值。另一种写容器元素算法可能会往容器中添加新元素,如fill的另一版本fill_n,要求从某个位置起连续加入n个元素,此时传递给算法的迭代器必须是插入迭代器。

3.       对容器元素重新排序的算法

对容器元素重新排序的算法不修改容器的大小,但是修改容器范围内的元素的值,例如前面的unique算法,或者如sort对容器范围内的元素进行重新排序等,此时传递给算法的迭代器不能是const的。

可以看到,不同算法对传递给他的迭代器有要求各有不同,find只要求其迭代器具有读的功能,而sort则要求其迭代器有读、写和随机访问的能力,根据算法要求可以将迭代器分成5个类别。

1.       输入迭代器:读,不能写;只支持自增运算,其解引用只能作为右操作数。

输入迭代器可用于读取容器中的元素,但不能保证能支持容器的写入操作,而且,输入迭代器只能顺序使用,一旦输入迭代器自增了,就无法再用它检查之前的元素。istream_iterator就是这样一种迭代器。

2.       输出迭代器:写,不能读;只支持自增运算,其解引用只能作为左操作数。

输出迭代器可用于向容器写入元素,但是不保证能支持读取容器内容。输出迭代器一般用做算法的第三个形参,标记起始写入的位置。例如copy_copy类的)算法使用一个输出迭代器作为它的第三个实参,将元素复制到输入迭代器指定的目标位置。ostream_iterator是一种输出迭代器。

3.       前向迭代器:读和写,只支持自增运算。

前向迭代器支持输入迭代器和输出迭代器提供的所有操作,除此之外,还支持对同一元素的多次读写。可以通过复制向前迭代器来记录序列中的一个位置,以便将来返回此处。

4.       双向迭代器:读和写,支持自增、自减运算

比前向迭代器多了运算。

5.       随机访问迭代器:读和写;支持完整的迭代器算术运算。

除了自增自减运算外,还提供加减一个整数获得指向新位置的迭代器,两个迭代器相减获得位置差等。

可以看到,迭代器的分类与其关联的容器类型相关。Vectordequestring迭代器是随机访问迭代器,而listmapset关联的迭代器是双向迭代器。

算法的形参一般有以下几种模式:

Alg( beg, end, … ) [beg, end)标记算法接收的容器的范围

Alg( beg, end, dest, … ) dest指定存储输出数据的目标对象

Alg( beg, end, beg2, … ) beg2标记算法的第二个接收范围的起始位置

Alg( beg, end, beg2, end2, … ) [beg2, end2) 标记算法的第二个接收范围。

算法可能还会接收一些函数作为其参数,以实现自定义的一些比较、筛选工具。

另外,需要注意的是,由于list的实现机理有别于vectordeque,为了充分利用它的结构特征以提高算法的效率,list中提供了<algorithm>中提供的一些操作,这些操作针对list的结构特征而设计,一般情况下优先选择list自己提供的函数。

c++ premier》第二部分总结起来基本上就是这样了。至于<algorithm>中提供的一些算法我也没有完全了解透,可能需要在应用中慢慢熟悉起来。还有就是有关提供给算法的函数参数,有些是Predicat,有些是BinPredUnaryPred,有些是Comp,有些是StrickWeakOrdering,这些内容不知道在哪里可以看到更多的介绍呢?

  • 大小: 102.8 KB
  • 大小: 30.1 KB
  • 大小: 69.2 KB
  • 大小: 30.1 KB
分享到:
评论

相关推荐

    STL学习总结

    关于STL学习的总结: STL就是Standard Template Library,标准模板库。这可能是一个历史上最令人兴奋的工具的最无聊的术语。从根本上说,STL是一些“容器”的集合,这些“容器”有list, vector,set,map等,STL也是...

    c++ -- stl 学习笔记

    stl 笔记 笔记描述 SGI STL 源码阅读笔记5 隐式类型转换总结.docx

    c++STL学习——排序算法技术总结和用法代码实例(一)

    通过几天学习终于将标准库中排序相关的函数学完了,虽然有些问题没有搞得非常透彻,但是这些算法用起来应该是没问题了!

    c++STL学习——各种容器的技术总结和用法代码实例

    经过几天的学习,c++标准库中所有容器的相关内容终于学完了,有不少心得和相关代码总结,决定与大家分享!

    c++个人学习笔记归纳总结.rar_C++STL_X9H3_c++个人笔记总结

    一、C++语言语法基础(6) 二、数据结构和算法 三、模板和STL 四、阶段项目

    C++STL总结.pdf

    本书涵盖目录书签,是每个学习者必备图书。欢迎下载哦

    C++STL的学习资料与笔记

    这里荔枝总结了一下下自己在学习C++的STL库的时候自己的一份学习笔记,这也是荔枝第一次上传资源到CSDN哈哈哈哈哈哈,如果有需要的话大家可以自行下载哈,希望能够帮助到大家。最后大家注意这里面的只是基础的东西,...

    c++STL学习——各种变异算法技术总结和用法代码实例(1)

    变异算法总结了标准库提供的对各种容器操作的变异算法,比如交换、查找、匹配、复制、删除等等。

    c++STL学习——排序算法技术总结和用法代码实例(二)

    通过几天学习终于将标准库中排序相关的函数学完了,虽然有些问题没有搞得非常透彻,但是这些算法用起来应该是没问题了!

    c++STL学习——各种变异算法技术总结和用法代码实例(2)

    非变异算法总结了标准库提供的对各种容器操作的变异算法,比如交换、查找、匹配、复制、删除等等。

    C++11特性总结

    gcc4.4.6,不能完整支持C++11,而且有内部有基础库早已支持了C++11 STL的部分功能),再加上自己的练习也写得少,了解仅是几点简单的皮毛,这里对C++11学习总结一番,期望对他人以及未来的我有点技术知识索引上的...

    C++学习总结

    C++学习总结 C++面向对象 C++ STL介绍 C++新知识扩展,内存池等

    标准模板库(STL)学习资料 (三个文件搞定入门)

    文件一:MYLIST.cpp 是一个使用STL中list的实例,在vs2010实测可以运行。 文件二:标准模板库(STL)学习---List容器 ( 转载 ).doc 我重新排版过,代码可复制。...文件三:多年学习C++STL经典总结_不看后悔。

    STL学习笔记

    这个学习笔记很不错的,能够让你在短时间内掌握stl,比一般的书籍上面要精辟易懂,个人觉得总结得很不错的。建议学C++的可以看看。

    EffectiveC++学习总结13页总结

    EffectiveSTL 学习STL、泛型编程的童鞋都应该瞧一瞧,很好的一本书,实践经验的总结,看完C++标准程序库后进阶的最好选择,本文是本人的个人总结,以备后用,如对你有帮助,不胜欢喜

    effective stl

    C++STL(Standard Template Library,标准模板)是一次革命,但是学习如何用它却是一个挑战。在本书中,Scott Meyers(两本最畅销的书《Effective C++》和《More Effective C++》的作者)揭示了专家总结的一些关键规则,...

    C++进阶课程讲义_v1.0.4.pdf

    7.4 总结 33 8、异常处理机制专题 33 8.1 异常处理的基本思想 34 8.1.1传统错误处理机制 34 8.1.2异常处理的基本思想 34 8.2 C++异常处理的实现 35 8.2.1异常基本语法 35 8.2.2栈解旋(unwinding) 39 8.2.3异常接口...

    C++ 后台工程师面试宝典

    万字长文 | STL 算法总结 数据结构与算法 数据结构与算法学习 LeetCode刷题笔记 数据密集型应用系统设计-读书笔记 第一章:构建可靠性、可扩展性、可维护性的应用 第二章:数据模型与查询语言 第三章:存储与检索 第...

    c++编程学习的技巧总结

    1、把C++当成一门新的语言学习(和C没啥关系)。 2、看《Thinking In C++》,不要看《C++编程思想》。 3、看《The C++ Programming Language》和《Inside The C++ Object Model》,不要因为他们很难而我们自己是初学者...

Global site tag (gtag.js) - Google Analytics