C++相关概念和易错语法(24)(map、迭代器分类)

1.map

在上篇文章中,我着重介绍了set,由于map和set同源,所以这次我会着重介绍map别于set的地方

(1)模板参数

set是以单一的key作为成员变量,而map是以pair作为成员变量,而pair的first作为key来决定插入位置,second与first一一对应。唯一需要注意的就是map插入pair后不能修改key只能修改value。逻辑上也是必须的,因为map底层的平衡树就是依靠key的大小来建立的,改了key之后这个结点的位置就不对了,整棵树都被毁了。(关联式容器)

(2)构造函数

构造函数和set一样简洁,注意传参仿函数对象和实例化类型要匹配,写不写无所谓,编译器其会自动生成

(3)insert

map的insert和set的一模一样,都是插入数据,返回pair

但是要注意map的insert对象是pair,下面有几种方式:

make_pair

make_pair是一个模板函数,有两个模板参数,可以通过传入参数的类型来推理并实例化,返回对应的匿名pair对象。值得注意的是,pair类型之间能够发生类型转换

这里可以看到,当make_pair的返回值类型和实例化类型不同时,可以发生隐式类型转换,转换的规则遵循first和second分别的转换规则

同样的,当插入pair的类型和map对应的pair类型不相同时,也能发生隐式类型转换。这也进一步验证了pair的本质只是一个存储数据的类型,其实这也依赖pair的operator=函数的支持。

除了make_pair构造以外,还有两种构造,都很好理解

(4)initializer_list

在C++11之后,所有的容器都支持了initializer_list

我们需要抓住当用初始化列表时,最外层{}是初始化列表的标志,({1, 2, 3})标志参数为初始化列表,初始化列表里又可以使用{}标志隐式类型转换,当初始化时可以省略=(如m3)

这里要慢慢消化,可以结合C++相关概念和易错语法(14)(初始化注意事项、vector、编译器向上查找规则)多练习

(5)map里没有流插入,pair也没有流插入,需要自己显式写

(6)find、lower_bound、upper_bound用法和set一模一样,返回值均为迭代器

count、erase返回值均为size_t,标志找到的元素个数

(7)map中iterator的使用

我们要抓住迭代器的本质是模拟指向存储元素的指针,存储的是pair,一个自定义类型,所以operator->就派上了大用场,这也是我们遇到的第一个operator->特殊处理后有实用的地方。

在set中由于是key单独作为存储数据,就没有operator->的概念,直接*it就得到数据了

我们要着重区分pair<iterator, bool>和iterator因为iterator指向的元素也是pair,在调用的时候分不清什么时候用.什么时候用->

(8)operator[]

这可以说是map中的一大难点,在vector、deque中的operator[]可以和*it互换,换句话说,我们之前接触的容器的operator[]本质上和数组的[]没什么区别,是随机迭代器的标配。

但map中是双向迭代器(功能少于随即迭代器)本应没有operator[],但STL让它支持operator[],这也使得它拥有了非同寻常的实现过程和功能。

下面是详细的分析

其中需要注意的是value的默认构造包括自定义类型和内置类型的默认构造,内置类型的默认构造如int(),它的返回值为0(所有编译器都是这样处理的)

理解了上面这些,我们就能利用map实现更多功能了

这已经不是传统opeartor[]的用法了,由于强制支持operator[],map可以向上面那样以很直观的方式插入修改和查找。

(9)范围for

迭代器模拟的是容器元素的指针,范围for返回的是元素的拷贝或引用

两者的底层都是迭代器,但是表层一个是模拟指针,一个是值或值的引用

在map中使用范围for建议都使用const auto&,因为如果返回值给e,那么每次都要构造一个pair,pair中还可能有自定义类型,这样开销太大了,直接用const引用效率会高很多

(10)multimap

multimap和map的区别类似于multiset和set的区别,都是支持多个key的存储,没有去重功能。但multimap没有operator[],因为同一个key可能对应不同value,没有意义。当使用迭代器遍历时同样遵循其元素pair的比较逻辑。

2.迭代器分类 

我们已经接触过了string、vector、list、deque、stack、queue、priority_queue、map、set、multiset、multimap这些基本容器,我们会发现这些容器中有的没有迭代器,有的迭代器支持随意+num,如string,有的就不行,如list。出现这样的原因是迭代器也有它的分类,不同的迭代器有不同的功能,有的算法库的函数只能用特定迭代器才能使用。

随机迭代器:string、vector、deque

双向迭代器:list、map、set、multiset、multimap

无迭代器:stack、queue、priority_queue

不同的迭代器有不同的功能,下面是随机迭代器和双向迭代器的比较

由于两种迭代器的功能差异较大,一种是逐个访问,一种可以跳跃访问,在一些对访问灵活度要求高的算法中,就不能传双向迭代器而只能传随机迭代器,如sort

所以在很多时候我们需要转换容器。看看下面的代码,尝试加深理解


#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

using namespace std;

struct Compare
{
	bool operator()(const pair<string, string>& p1, const pair<string, string>& p2) const
	{
		return p1 < p2;
	}
};

int main()
{
	map<string, string> m;

	m["Huawei"] = "华为";
	m["Xiaomi"] = "小米";
	m["Apple"] = "苹果";
	m["Samsung"] = "三星";

	vector<pair<string, string>> v(m.begin(), m.end());
	sort(v.begin(), v.end(), Compare());

	for (const auto& e : v)
	{
		cout << e.first << " " << e.second << endl;
	}

	return 0;
}

结果是

但是这里有个疑问,为什么可以用map的迭代器来初始化vector?有没有要求呢?这就需要引出另外几个迭代器:输入迭代器

此外还有剩余两个迭代器:输出迭代器、前向迭代器

我们重点区分双向迭代器和随机迭代器,很多容器都是这两种迭代器之一。在特定情况下我们也知道去切换容器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值