1.关联容器是通过关键字来保存和访问数据的。关联容器分为两大类:map和set。其中,map是通过键值对来操作的,这里的键就是关键字,值就是对应的数据。
例如:
map<int ,int> m;定义了一个空的map变量m,它的关键字类型是int,关键字对应的值的类型是int。可以将map理解成为函数,关键字是自变量,关键字对应的值是因变量。
set,这是一个集合类。它的关键字和值是相同的。也就是说定义了一个set,它的关键字和值是同一个值。例如:
set<int> s;定义了一个空的set类型的变量s,它里面可以存储int类型的数据,它的关键字和值都是同一个,类型都是int。set和数学上的集合是一样的,都有一个特点,唯一性。也就是说,set中的元素,一般不能重复出现。因此,我们有时候使用set,来判断一个关键字是否在一个某一个集合中。
2.map和set有很多种类:它们通过前面的修饰词来加以区分。修饰词主要有:unordered,multi,一个表示无序,另外一个表示关键字可以重复。例如:unordered_multiset表示,一个无序的,关键字可以重复的set。这里的无序的实现数据结构是哈希函数。
map和set都是模板。
3.关于定义关联容器,可能当你去读C++ primer这本书时,会发现,在定义关联容器时有好多方法。其实,通过我的时间发现,这和你的编译环境,编译器是有很大的联系的,又看看,你的编译器对C++11支持多少。
例如:
定义:map<string,string> word={ {"A","a"},{"B","b"}};//对于这样的定义,不同的编译器,编译之后的结果也是不一样的。想VS2012好像就不能支持。
4.因为,关联容器的关键字和对应值的类型,没有强制的限制,也就是说,自己定义的类也可以当做关键词或者对应的值的类型来使用,但是,我们要注意,无论哪一种类型,要支持比较运算符<,或者要支持比较方法。如果不支持,麻烦自定义。
5.pair类型:
在之前我们说cocos2dx中读取excle文件时,最后提到了pair类型,这里我们对其进行更加详细的说明。
pair类型,这是一个标准库类型。pair类型,保存两个数据,在初始化的时候,需要传递两个类型。pair的两个数据成员是公共的。
6.关联容器的操作:
6.1额外的类型别名:
value_type:对于set来说,这个和关键字的类型是一样的。对于map来说,这个的类型是:pair<const key_type,mapped_type>
例如:
map<int ,int >::value_type var;//var的类型就是pair<const int,int>。
mapped_type:每个关键词关联的类型。这个只使用与map,说白了,就是值的类型
例如:map<int ,int >::mapped_type var;//var的类型就是int
key_type:就是关键字的类型
6.2关于map的操作.
6.2.1要向map中添加元素,这个元素的类型,必须是pair类型。正如上面所说的,map中value_type是pair类型。因此,就像之前讲过的,要构造一个pair类型的数据,例如:make_pair<i,j>这就是构造一个pair类型的数据。
例如:
map<int,int> m1;//不能重复关键字
auto it=m1.begin();
map<int ,int>::key_type i;
int j=10;
for(int i=0;i<10;i++)
{
auto p=m1.insert(make_pair(i,j));
j++;
}
添加元素,使用函数insert()。
但是,要特别注意:insert的返回值问题。
template<class _Valty>
typename enable_if<is_convertible<_Valty, value_type>::value,
_Pairib>::type
insert(_Valty&& _Val)
{ // try to insert node with value _Val, favoring right side
_Nodeptr _Newnode = this->_Buynode(_STD forward<_Valty>(_Val));
return (_Insert_nohint(false,
this->_Myval(_Newnode), _Newnode));
}
上面是insert的源码。编译环境VS2012
insert返回的值依赖于容器类型和参数。
如果是关键字不能重复的,insert返回一个pair类型值。这个值的first数据成员保存的是一个指向要插入元素的迭代器,second数据成员,是一个bool值,用来表示要插入的值,是否插入成功。
例如:
map<int,int> m1;//不能重复关键字
auto it=m1.begin();
map<int ,int>::key_type i;
int j=10;
for(int i=0;i<10;i++)
{
auto p=m1.insert(make_pair(i,j));
if(p.second)
cout<<"插入的值:"<<p.first->second<<endl;
j++;
}
在这段代码中,我们将i,j插入到map中。在这里,p的类型就是一个pair类型.first数据成员,是一个迭代器,指向插入元素的一个迭代器。这个迭代器中,存放的就是要插入的这两个数i和j的值。p的second元素是一个bool类型的数据,表示是否插入成功。
这段代码,单步调试的结果:
最后的结果值:
6.2.2其次,就是迭代器的问题。
当解引用一个关联容器的迭代器是,我们将得到一个值的value_type的一个引用。
在map中,value_type是一个pair。而且,first还是const类型的,所以,不能修改first指向的数据。
在set中,set和map差不多,也是只能读取,不能修改。
例如:
for(auto it=m1.begin();it!=m1.end();it++)
{
cout<<"first:"<<it->first<<"second:"<<it->second<<endl;
要注意,当使用迭代器遍历map和set时,迭代器是按照关键字的升序顺序来遍历的。
6.2.3 删除元素。
删除元素,使用ereas函数,传入的参数可以是关键字,迭代器,迭代器的范围。也就是说,通过这个函数,可以删除一个范围的元素。
例如:
m1.erase(1);//删除对应关键词的元素
这一步操作之后,会将关键字为1,这个键值对删除。
6.2.4下标运算符和访问元素
map是可以支持下标运算符的。set是不支持下标运算符的。
例如,之前定义的m1这个变量,如果,我们这样操作:m1[11]。显然,我们没有11这个关键字,但是如果使用下标运算符之后,他会将11这个关键字添加到m1这个map中,而且,还会初始化。
因此,我们要知道,一个关键字,是否在一个map中,最好使用find和cout函数。
这两个函数的源码:
// 返回关键字的个数
// size_type count(const key_type& _Keyval) const
// { // count all elements that match _Keyval
// _Paircc _Ans = equal_range(_Keyval);
// size_type _Num = 0;
// _Distance(_Ans.first, _Ans.second, _Num);
// return (_Num);
// }
p.count(1);
count这个函数,是判断这个关键字在map中,是否存在,而且,存在几个。
//这个函数适合检查该元素是否在关联容器中,传递的参数是关键词的类型
//源码
//iterator find(const key_type& _Keyval)
//{ // find an element in mutable sequence that matches _Keyval
//iterator _Where = lower_bound(_Keyval);
//return (_Where == end()
// || _DEBUG_LT_PRED(this->_Getcomp(),
// _Keyval, this->_Key(_Where._Mynode()))
// ? end() : _Where);
//}
auto temp= p.find(1);
find函数,只关心关键字是否存在在map中。
这就是今天我们要和大家分享的关于C++关联容器的知识,欢迎大家指正!