您的位置:首页 > 编程语言 > C语言/C++

c++Primer,九,十,顺序容器,关联容器

2015-03-16 21:59 309 查看
    这两章都是讲标准库的容器,所以放在一起了,标准库的东西,用到的时候看看使用说明API,用几次也就会了。我也没怎么注意这些。

   在讲这之前,曾经学习过vector容器和迭代器,现在回顾下:数组是同种元素的集合,无论是几维数组(准确的说,只有一维数组),最终通过下标操作符[ ]得到的值都是这种类型,[ ]里的是这个元素的索引,计算机内部具体是如何分配内存的,这就得深入了,现在可以这么想(不一定对),int arr[ 88 ],在栈上开辟了88 * 4个字节连续的内存空间,每4个字节是一个int,这在编译的时候就确定了,然后就可以通过[ ]操作符来得到索引对应的值,另外可以把数组名赋值给一个int * p指针,指针指向的是int 类型,也就是arr的第一个元素,现在就可以通过p++,来每次使p指向下4个字节后的一个int,因为开辟的是连续的,所以这个int是确定存在的;如果是动态数组
int * parr = new int
,new运算符是开辟内存运算符,此时在堆上开辟n * 4个字节连续的内存空间,使
4000
用parr和使用arr是完全没有差异的,都可以通过[ ]和指针来访问。

   现在回顾下在学习数据结构的时候,曾经使用一个结构体链表,用一个结构体的最后一个元素保存下一个结构体对象的地址,这样链接起来,定义一个该结构体指针指向第一个结构体对象,然后便可以通过结构体里的下个结构体对象的指针依次得到各个元素,这里是通过元素里的值得到下个地址,,没有熟悉的指针p++了,因为地址不是连续的;如果这里定义的是一个结构体数组,那么无须结构体内保存下个结构体对象的地址,直接像数组一样,[ ]操作符或者指针p++都行,因为地址是连续的。

   在前面的vector,类似于数组,它的内存也是连续的,但是它支持动态增长,它的内存是变化的,所以说,当你增加一个元素或者删除一个元素之后,可能所有的元素的地址都发送变化了,同样说到迭代器类似于指针,依然熟悉的指针p++,但是要注意了,如果内存发生了变化,这个本应该在连续内存上操作的特性就可能发生运行时错误,于是就是说迭代器失效了。

好了,下面开始真题。

第九章 顺序容器

标准库定义了三种顺序容器类型:vector、list 和 deque(是双端队列“double-ended queue”的简写,发音为“deck”)。它们的差别在于访问元素的方式,以及添加或删除元素相关操作的运行代价。标准库还提供了三种容器适配器(adaptors)。实际上,适配器是根据原始的容器类型所提供的操作,通过定义新的操作接口,来适应基础的容器类型。顺序容器适配器包括 stack、queue 和 priority_queue 类型。容器只定义了少量操作。大多数额外操作则由算法库提供。

eg:

#include <vector>

#include <list>

#include <deque>

vector<string> svec;

list<int> ilist;

deque<int> ideque;

他们都是模板类,具体他们的定义,初始化等查看容器说明,详细的很。。。。。

容器内的元素都是有类型约束的,大多数都可以作为元素,只要该类型支持赋值元素,可复制就可以。

使用容器,搭配的方案是使用容器相对应的迭代器,类似于 vector<int>::iterator,术语是容器的类型别名,反正就是种类型啦。

基本迭代器可操作范围是容器的第一个元素的地址和容器的最后一个元素的下一个地址,这里说的下一个,是以容器所存储的类型大小为单位的下一个。迭代器一般都支持*,==,++等操作,那么问题来了,用到了++,我们是不是说内存就一定是连续的呢?不然怎么通过++得到下一个元素的地址呢?不过,看样子还真是连续的,如果这么说,那么下一章的map呢?它也有迭代器,也支持++,那它的地址作何解释?所以说呢,不能通过++就确定迭代器的所属容器的内存情况,万一迭代的++是重载的,在内部完成了类似链表的那种指针指向下个元素地址呢。这些只不过自己无聊而已,具体还得深究下STL源码呀。。。

不同的容器的迭代器还具有自己特有的操作,具体迭代器的使用,不细说,反正就是个指针就得了,又没有什么特殊的函数。另外迭代器失效的问题,深究起来,我是说vector这种顺序内存的容器,删除一个元素后为什么会失效,书上说,任何指向已删除元素的迭代器都具有无效值,毕竟,该迭代器指向了容器中不再存在的元素。无效值就无效值,如果删除操作的时候,后面的值会自动往前移的话,就更纠结了,为啥会失效啊,这里还是我太幼稚呀,具体想不通,书上说,vector的内存占用只会增多,不会减少,即使删除了元素,它的内存占用还是不会变的,一直到释放vector,所以对于vector,我们用的迭代器,或者[
]来访问的永远都是已有的数据,当用它指向一个被删除的元素时,按逻辑来说,就应该是失效的,所以我们的编译器帮我们报错了,如果可以忽略错误,那么对内存的所有操作都是可行的,这有篇文章不错,感谢他,http://blog.csdn.net/zhongjiekangping/article/details/5624922。好了,对于这个问题,想深究都得看源码了,反正用的时候,debug模式下注意失效就好了。

下面是API了,没什么好说的,每种顺序容器都提供了一组有用的类型定义以及以下操作。主要有添加,删除,访问元素等等。。

vector:提供下标访问每个约束。

list:list采用的是链接存储而不是连续存储,所以本质上它不支持下标方式(也就是按偏移量)访问。

deque倒是不常用。

本章还继续细谈了string,它可作为是字符容器,也支持某些容器操作,甚至,它也有自己的迭代器。。。

再往下,又讲到容器适配器。适配器(adaptor)是标准库中通用的概念,包括容器适配器、迭代器适配器和函数适配器。本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制。 容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。具体也不懂。。哈哈

本章是标准库里的顺序容器,标准库嘛,不想用就不用喽,只是用了效率更高。

第十章 关联容器

关联容器(Associative containers)支持通过键来高效地查找和读取元素。两个基本的关联容器类型是 map set。map 的元素以键-值(key-value)对的形式组织:键用作元素在 map 中的索引,而值则表示所存储和读取的数据。set仅包含一个键,并有效地支持关于某个键是否存在的查询。

首先引出的是pair类型,包含了一对键--值,用法如下:

pair<string, string> author("James", "Joyce");

author.first == "James" && author.second == "Joyce"

。。next_auth = make_pair(first, last);

OK,够了。。。。唉,怎么能长进,怎么能学好。。。。

map容器:

map<string, int> word_count;

它的键会用来自排序,所以约束条件是键具有 < 操作的类型。它的一个元素,是一个pair类型的键--值。它支持下标访问,但它不是常规的下标偏移量计算元素地址哦,它可能重载了[]吧,看源代码去吧,我没看。。。

关于map的定义初始化,添加,删除元素,遍历访问等用法,一搜一大堆,不打例子了,唉,眼高手低啊。。。。

set容器:

map 容器是键-值对的集合,好比以人名为键的地址内容。相反地,set 容器只是单纯的键的集合。

set<int> iset;

关于set的定义初始化,添加,删除元素,遍历访问等用法。。。。呵呵。另外它不支持[ ]操作。

本章后面还说到,map 和 set 容器中,一个键只能对应一个实例。而 multiset 和 multimap类型则允许一个键对应多个实例。

然后就结束吧,太多的标准库用法学习,使目前学习c++语言出现了停滞。。。这些库,不用,依然可以写代码,可是语法没学会,还怎么写代码,虽然说学习语法知识一个星期就够了,但是后面的新的自定义数据类型“类”,还有实现面向对象编程所需的继承和多态技术,也是很复杂的啊,而且当分析到内存到底在干啥的时候,又迷茫了。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: