C++:map的operator[]的底层实现及其使用

map的operator[]的底层实现

首先我们都知道map的operator[]可以通过key值找到对应的value值。

成员类型 含义
key_type The first template parameter (Key)
mapped_type The second template parameter (T)
value_type pair

那么这里我们了解一下operator[]的底层实现,直接看可能有点迷:
C++:map的operator[]的底层实现及其使用_第1张图片
那我们可以发现其实operator[]底层是用insert来实现的。
那么inset的返回值又是什么呢?我们一起来看看:

pair<iterator,bool> insert (const value_type& val);

查cplusplus网站阅读insert的返回值(大家可以自己理解一哈):The single element versions (1) return a pair, with its member pair::first set to an iterator pointing to either the newly inserted element or to the element with an equivalent key in the map. The pair::second element in the pair is set to true if a new element was inserted or false if an equivalent key already existed.

我们发现inset的返回值其实是一个pair,这个pair的key值其实是一个迭代器,这个迭代器是指向新插入这个位置的迭代器,value值其实是一个bool值,表示插入成功还是失败,原来没有这个key值插入会成功返回true,已经存在这个key值插入会失败,返回false。

那么再回来!我们再看看这个operator[]的底层实现,并添加一个栗子:
C++:map的operator[]的底层实现及其使用_第2张图片
(红色框框)首先这里创建了一个pair,这个pair的key值为传参传过来的k,value是一个mapped_type的value,暂时为空待定。
(绿色框框)调用insert插入这个pair。插入这个pair会返回一个pair。pair的第一个值就是这个位置的迭代器,但是因为这个key值已经存在了,因此pair的value为false。
(粉色框框)this指针取到这个pair的first,就是我们刚刚看到的insert的返回值pair的key值,也就是插入位置的迭代器。
最后解引用之后再取到这个位置的second值,也就是这个位置的value值。然后函数的返回值为mapped_type的引用。

综上所述,operator[]返回的是这个key值所对应的value的引用。因此operator[]可以通过key值找到对应的value值,然后对这个value值进行修改操作。

举个栗子:
前情提要:dict是一个 map 的类型

dict.insert({ "left", "左边" });
dict["left"] = "剩余";

栗子中就是这样的:“left”这个key值已经存在,因此insert返回的是这个位置的迭代器和一个false(false是因为key值存在插入失败),取到这个迭代器,然后解引用取到迭代器的second,也就是value并返回value的引用,因此dict["left"]返回的就是"左边"这个字符串的引用,那么在执行赋值操作,也就是改变了这个“left”所对应的value值,此时“left”的value值就变成了“剩余”。

那operator[ ]怎么完成插入的鸭?

那么operator[]又可以完成数据的插入,这又是怎么实现的鸭????
那么不要急,我们继续分析operator[]的底层实现ヾ(●´∀`●)
C++:map的operator[]的底层实现及其使用_第3张图片
当我们传过来的key值是map里没有的,那么这里创建了一个pair,这个pair的key值就是我传过来的key值,但是此时这里的mapped_type的value,暂时为空待定。调用了insert,因为这个key值没有,所以完成了插入,insert的返回值为新插入这个位置的迭代器和true(true表示插入成功),然后取到这个迭代器,解引用之后拿到这个key值的value(此时value值为空),返回的就是这个空的value。

举两个栗子:

前情提要:dict是一个 map 的类型

dict["banana"];

如上就是插入了一个key为“banana”,value为“\0”的一个pair。

dict["key"] = "关键字";

如上,首先经过dict["key"]之后,就成功插入了一个key值为“key”,value为“\0”的一个pair,返回值是“\0”的引用,然后在赋值操作,也就是将“\0”变成了“关键字”,这样就完成了一个pair的插入。

总结一下:operator[]可以查找到对应key值的value,如果key值没有的话,也可以进行插入操作。

一道思考题~~

统计每种水果出现的次数
“苹果”, “苹果”, “梨”, “苹果”, “香蕉”, “梨”, “香蕉”, “香蕉”, “苹果”, "西瓜"

有以下三种解决方法:

方法一:

定义一个map,类型为map ,key值就是水果(不允许重复),value值就可以记录出现的次数。

去寻找字符串在不在这个map中,如果这个水果在,那么直接给这个pair的second++,如果不在,就插入这个水果,把second设置为1。

string str[] = { "苹果", "苹果", "梨", "苹果", "香蕉", "梨", "香蕉", "香蕉", "苹果", "西瓜" };
map<string, int> count_map;
for (const auto &s : str)
{
	auto tmp = count_map.find(s);
	if (tmp != count_map.end())
	{
		tmp->second++;
	}
	else
	{
		count_map.insert({ s, 1 });//返回的是pair::iterator ,bool>
	}
}
for (const auto &e : count_map)
{
	cout << e.first << ":" << e.second << endl;
}
方法二:

定义一个map,类型为map ,key值就是水果(不允许重复),value值就可以记录出现的次数。

因为insert的返回值中value是一个bool,表示插入成功还是失败,所以我们直接插入这个水果和1(表示这个水果出现了一次)。我们可以通过这个bool值判断这个水果在不在这个map里。如果在,那么insert的返回值的value是一个false,那么直接利用inset的返回值中的key(对应位置的迭代器)找到对应位置的value,直接++即可。如果不在那么insert的返回值的value是一个true,此时就完后了这个水果和1次的插入。

string str[] = { "苹果", "苹果", "梨", "苹果", "香蕉", "梨", "香蕉", "香蕉", "苹果", "西瓜" };
map<string, int> count_map;
for (const auto &s : str)
{
	pair<map<string,int>::iterator ,bool> ret = count_map.insert(pair<string, int>(s, 1));
	if (!ret.second)
	{
		(ret.first)->second++;
	}
}
for (const auto &e : count_map)
{
	cout << e.first << ":" << e.second << endl;
}
方法三:

定义一个map,类型为map ,key值就是水果(不允许重复),value值就可以记录出现的次数。
我们可以利用这个operator[]来解决这个问题,代码非常简单。
直接遍历这个水果字符串数组,然后调用operator[],因为operator[]有就返回value的引用,没有就插入之后返回value的引用,那么当遍历这些水果的时候,如果这个水果在,那么直接给value++,如果这个水果不在,那么返回的是0的引用,直接++就变成了1,表示出现一次,后序在插入同样的水果时,插入失败,直接给value++,逻辑也没有任何问题,因此这个方法最为简单,推荐~

string str[] = { "苹果", "苹果", "梨", "苹果", "香蕉", "梨", "香蕉", "香蕉", "苹果", "西瓜" };
map<string, int> count_map;
for (auto s : str)
{
	count_map[s]++;
}
for (const auto &e : count_map)
{
	cout << e.first << ":" << e.second << endl;
}

三种方法都可以正确运行,结果如下:
C++:map的operator[]的底层实现及其使用_第4张图片

你可能感兴趣的:(C++)