C++ primer第二次阅读学习笔记(第10章:关联容器)

来源:互联网 发布:单片机几个p口的区别 编辑:程序博客网 时间:2024/05/16 14:04

第十章:关联容器

关联容器和顺序容器的本质差别在于:关联容器通过键存储和读取元素,而顺序元素通过元素在容器中的位置顺序存储和访问元素。虽然关联容器的大部分行为与顺序容器相同,但其独特之处在于支持键的使用。

关联容器通过键来高效的查找和读取元素,两个基本的关联容器为mapsetmap的元素以键---值对的形式组织,键用作元素在map中的索引,而值表示存储和读取的元素。set仅包含一个键,并有效地支持关于某个键是否存在的查询。

如果希望存储不同值的集合,使用set比较合适,而map容器更适用于需要存储每个键所关联的值得情况。如在做文本处理时,可使用set保存忽略的单词,而字典则是map的一种很好的应用。单词本身是键,而释义则是值。

pair包含两个数值,是一种模板类型。在utility头文件中定义。在创建pair类型时必须提供两个类型名。这两个类型不必相同。如

1:pair<T1,T2> p1;

2:pair<T1,T2> p1(v1,v2);

3:make_pair(v1,v2);//v1,v2的值创建新的pair对象。

如果在创建pair对象时不提供初始化式,则调用默认构造函数对其成员进行初始化。

pair类型的使用很繁琐,可以使用typedef简化其声明:typedef pari<string,string> author;

author  A("jim","green");

可以直接访问pair对象数据成员,其成员都是公有的。分别命名为firstsecond

关联容器共享大部分但并非全部顺序容器操作。如

C<T> c;

C<T> c1(c2);

C<T>c(b,e);//b,e为一对迭代器。

关联容器无法通过容器大小来定义,因为那样就无法知道键对应的值是什么。

容器的元素根据次序排列,因此在迭代器遍历关联容器时,我们可确保键的顺序访问元素而与元素在容器的存放位置完全无关。

map可以理解为关联数组。可使用键作为下标来获取一个值。

要使用map对象必须使用map头文件。同时必须分别指明键和值的类型。在使用关联容器时,键不但有一个类型,而且还有一个相关的比较函数。默认情况下,标准库使用键类型定义<操作符来实现键的比较。可以通过重写默认的操作符,并提供自定义的操作符函数,在后面的模板中会有介绍。所用的比较函数必须在键类型定义严格弱排序。

对于键类型,唯一的要求就是必须支持<操作符,至于是否支持其他的关系或相等运算则不作要求。如不能定义一个list<int>::iterator关联int对象,因为listiterator仅支持除==和!=运算符,不支持<运算符(list中的元素不是顺序存储,存储位置不能作为在list中的相对位置)。而vector deque却可以。

map对象的元素是键--值对。它包含两个部分:键以及键关联的值。mapvalue_type就反映这个事实。value_type就是存储元素的键以及值得pair类型,而且键为const

map(T1,T2>::value_type    map对象元素类型。pari<T1,T2>

map(T1,T2>::mapped_type  键关联的值得类型T2

map(T1,T2>::key_type      用作索引的键的类型T1

map迭代器进行解引用将获得一个引用,指向容器中一个value_type类型的值,其为pair

pairfirst存放键,但它为constsecond成员存放值。

可以通过insertmap容器中添加元素。也可以通过键作为下标获取元素,然后给获取的元素赋值。

通过以键作为下标访问map对象。

map<string,int>word_count;

word_cout["Jim"]=1;

将发生以下事情:

1:word_count中查找键名为Jim的元素,没有找到。

2:将一个新的键值对插入到word_count中。它的键是const string类型,保存Jim,而它的值采用值初始化,为0

3:将这个新的pair对象插入word_count

4:读取新插入的元素,修改为1

因此使用键作为下标来访问不存在的元素将会导致在map容器中添加新的元素。所关联的值采取值初始化,类类型的元素用默认构造函数初始化,而内置类型将会被初始化为0。如果该键已存在则map的下标运算与vector的下标运算行为相同。

map::insert成员函数使用。

m.insert(e);//epair对象。

m.insert(beg,end);//begend为一对迭代器

m.insert(iter,e);//epair对象。iter为迭代器。

1word_count.insert(map<string,int>::value_type("Jim",1));

2word_count.insert(make_pair<string,int>("Jim",1));

3word_count.insert(pair<string,int>("Jim",1));

其中13大同小异。因为value_type就是由pair<string,int>定义而来。

使用insert可以避免下标操作符带来的副作用:不必要的初始化。

当要插入的元素的键值已经存在,则不作任何操作。因此当传递一对迭代器时,并不说明该迭代器范围内的元素都被插入。

m.insert(e)将返回一个pair类型的对象。first为一迭代器,指向map中具有相应键的元素(pair类型),secondbool类型。如果该键已在容器中,则返回的boolfalse,如该键不在容器中,则插入新元素,bool值为true。即bool指示是否插入该元素到容器中。这两种情况下返回的迭代器都指向具有给定键的元素(pair类型)。如

map<string,int> word_count;

pari<map<string,int>::iterator,bool>ret

                    =word_count.insert(make_pari("Jim",1));

m.count(k);//返回mk出现的次数。

m.find(k),//如果存在以k为索引的元素则返回指向该元素的迭代器,否则指向超出末端迭代器。

如:map<string,int>::iterator it=word_count.find("Jim");

if(it!==word_count.end())

    //存在。 

因为map为单值映射,因此count的返回值非01.这可以用于判断某个键是否存在。而对于multimapcount的返回值则可能会>1

map容器中删除元素的erase操作有三种变化形式:

1m.erase(k);//删除键为k的元素,返回删除个数。

2m.erase(p);//删除迭代器p所指向的元素,返回void

3m.erase(b,e);//删除一段迭代器范围内元素。返回void

map容器是键--值对的集合,好比是以人名为键的地址和电话号码。而set仅是单纯键的集合。

为了使用set必须包含set头文件,set不支持下标操作符,没有定义mapped_type类型,value_type是与key_type相同的类型。都是指容器中的元素类型,且键值必须唯一不能修改。

set容器中的元素必须唯一,如果以一段范围的迭代器初始化set容器,对于每个键,只添加一个元素,重复的元素不再添加。如

char *name={"Jim","Hellen","Green","Tom","Kate","Jim","Tom"}

set<string> Name(name,name+7);

对于此句由于JimTom均出现两次,但插入到set容器内时,重复元素不会被添加多次。此时set5个元素。

set内添加元素使用insert,与map类似。

1m.insert(a);//a为键值。返回pair对象与map相同。

2m.insert(b,e);//b,e为迭代器范围。

set容器不提供下标操作符,要从容器内获取元素可以使用find运算,它返回指向该元素的迭代器(如果存在的话)。在获得某元素的迭代器之后只能对其做读操作不能修改,因为键是const类型的。

count返回值只能为01

     各种容器的许多操作都是类似的,C++primer在介绍set时省略了很多,详细用法请参考其他资料。

multimapmultiset

mapset容器中一个键只能对应一个实例。而multimapmultiset则允许一个键对应多个实例。如在电话簿中,每个人可能有单独的电话号码列表。multimapmultisetmapset具有相同的头文件分别为setmap

    multimap不支持下标运算,这一点是个例外。其他操作multimapmultisetmapset操作相同。

 inserterase同样适用于multisetmultimap

erase将删除拥有该键的所有元素,并返回删除元素个数。

关联容器的元素是按顺序存放的,而具有相同键的元素在容器中相邻存放。这一点非常重要。如

multimap<string,string>::iterator iter=author.find("Jim");

for(unsigned i=0;i!=author.count();i++)

{

  cout<<iter->second<<endl;

   iter++;
   }

首先确定键为Jim的元素个数。然后获得指向第一个该键所关联的元素的迭代器。依次获得具有该键的所有元素。因此明白:在multimap中同一个键所关联的元素必然相邻存放是突破点。

另一个替代方案为:

m.lower_bound(k);//返回迭代器,指向键不小于k的第一个元素。

m.upper_bound(k);//返回迭代器,指向键大k的第一个元素

m.equal_bound(k);//返回pair对象,first成员等价于m.lower_bound(k),second成员等价于m.upper_bound(k)

所有这些操作都需要传递一个键。

在同一个键上调用lower_boundupper_bound将产生一个迭代器范围,指出该键所关联的所有元素。如该键在容器中存在,则将会获得两个不同的迭代器。如果该键不存在,这两个操作将指向同一个迭代器,指向依据元素的排列顺序应该插入的位置。

调用equal_range可以取代lower_boundupper_bound函数。它返回存储一对迭代器的pair对象。如果该值存在,则pair对象中的第一个迭代器指向该键关联的第一个实例,第二个迭代器指向该键关联的最后一个实例的下一位置。如果找不到匹配的元素,则pair对象中的两个迭代器都将指向此键应插入的位置。

原创粉丝点击