首先我們都知道STL中的map的operator[]可以通過key值找到對應的value值的:
<code>#include <iostream>
#include /<iostream>/<code>
這裡我們要了解map模板類operator[]的底層實現,首先要了解一下map模板類的數據成員和成員函數。(參考:http://www.cplusplus.com/reference/map/map/)
如:
<code>key_type: The first template parameter (Key)
mapped_type: The second template parameter (T)
value_type: pair<const>
operator[]():Access element (public member function )
insert():Insert elements (public member function )/<const>/<code>
其中operator[]()的實現原型:
(參考:http://www.cplusplus.com/reference/map/map/operator[]/)
<code>mapped_type& operator[] (const key_type& k)
{
return (*((this->insert(make_pair(k,mapped_type()))).first)).second;
}/<code>
那我們可以發現其實operator[]底層是用insert()來實現的。
(參考:http://www.cplusplus.com/reference/map/map/operator[]/)
那麼inset()的返回值又是什麼呢?我們一起來看看insert()的函數原型:
<code>pair<iterator> insert (const value_type& val);/<iterator>/<code>
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_type,value值其實是一個bool值,表示插入成功還是失敗,原來沒有這個key值插入會成功返回true,已經存在這個key值插入會失敗,返回false。
我們再回頭看看這個operator[]的底層實現:
make_pair(k,mapped_type())(紅色框框):表示創建了一個pair,這個pair的key值為傳參傳過來的k,value是一個mapped_type的value,暫時為空待定。
insert(make_pair(k,mapped_type()))(綠色框框):調用insert插入這個pair。插入這個pair會返回一個pair。pair的第一個值就是這個位置的迭代器,但是因為這個key值已經存在了,因此pair的value為false。
(this->insert(make_pair(k,mapped_type()))).first(粉色框框):this指針取到這個pair的first,就是我們剛剛看到的insert的返回值pair的key值,也就是插入位置的迭代器。
最後解引用迭代器之後再取到這個位置pair的second值,也就是這個位置的value值。然後函數的返回值為mapped_type的引用,因為是引用,所以可以做為左值而更新其value。
綜上所述,operator[]返回的是這個key值所對應的value的引用。因此operator[]可以通過key值找到對應的value值,然後對這個value值進行修改操作。
舉個例子:
<code>map<string> dict;
dict.insert({ "left", "左邊" });
dict["left"] = "剩餘";/<string>/<code>
“left”這個key值已經存在,因此insert返回的是這個位置的迭代器和一個false(false是因為key值存在插入失敗),取到這個迭代器,然後解引用取到迭代器的second,也就是value並返回value的引用,因此dict["left"]返回的就是"左邊"這個字符串的引用,那麼在執行賦值操作,也就是改變了這個“left”所對應的value值,此時“left”的value值就變成了“剩餘”。
那麼operator[]又可以完成數據的插入,這又是怎麼實現的呢?
我們繼續分析operator[]的底層實現。
當我們傳過來的key值是map裡沒有的,那麼這裡創建了一個pair,這個pair的key值就是傳過來的key值,但是此時這裡的mapped_type的value,暫時為空待定。調用了insert,因為這個key值沒有,所以完成了插入,insert的返回值為新插入這個位置的迭代器和true(true表示插入成功),然後取到這個迭代器,解引用之後拿到這個key值的value(此時value值為空),返回的就是這個空的value。
再舉個例子:
<code>map<string> dict;
dict["banana"];/<string>/<code>
如上就是插入了一個key為“banana”,value為“\\0”的一個pair。
<code>dict["key"] = "關鍵字";/<code>
如上,首先經過dict["key"]之後,就成功插入了一個key值為“key”,value為“\\0”的一個pair,返回值是“\\0”的引用,然後再賦值操作,也就是將“\\0”變成了“關鍵字”,這樣就完成了一個pair的插入。
總結一下,operator[]可以查找到對應key值的value,如果key值沒有的話,也可以進行插入操作。
實例:統計每種水果出現的次數。
“蘋果”, “蘋果”, “梨”, “蘋果”, “香蕉”, “梨”, “香蕉”, “香蕉”, “蘋果”, "西瓜"
有以下三種解決方法:
方法一:
定義一個map,類型為map<string> ,key值就是水果(不允許重複),value值就可以記錄出現的次數。/<string>
去尋找字符串在不在這個map中,如果這個水果在,那麼直接給這個pair的second++,如果不在,就插入這個水果,把second設置為1。
<code>string str[] = { "蘋果", "蘋果", "梨", "蘋果", "香蕉", "梨", "香蕉", "香蕉", "蘋果", "西瓜" };
map<string> count_map;
for (const auto &s : str)
{
\tauto tmp = count_map.find(s);
\tif (tmp != count_map.end())
\t{
\t\ttmp->second++;
\t}
\telse
\t{
\t\tcount_map.insert({ s, 1 });//返回的是pair/<string>/<code>
方法二:
定義一個map,類型為map<string> ,key值就是水果(不允許重複),value值就可以記錄出現的次數。/<string>
因為insert的返回值中value是一個bool,表示插入成功還是失敗,所以我們直接插入這個水果和1(表示這個水果出現了一次)。我們可以通過這個bool值判斷這個水果在不在這個map裡。如果在,那麼insert的返回值的value是一個false,那麼直接利用inset的返回值中的key(對應位置的迭代器)找到對應位置的value,直接++即可。如果不在那麼insert的返回值的value是一個true,此時就完後了這個水果和1次的插入。
<code>string str[] = { "蘋果", "蘋果", "梨", "蘋果", "香蕉", "梨", "香蕉", "香蕉", "蘋果", "西瓜" };
map<string> count_map;
for (const auto &s : str)
{
// insert()的返回值是一個pair
\tpair/<string>/<code>
方法三:
定義一個map,類型為map<string> ,key值就是水果(不允許重複),value值就可以記錄出現的次數。/<string>
我們可以利用這個operator[]來解決這個問題,代碼非常簡單。
直接遍歷這個水果字符串數組,然後調用operator[],因為operator[]有就返回value的引用,沒有就插入之後返回value的引用,那麼當遍歷這些水果的時候,如果這個水果在,那麼直接給value++,如果這個水果不在,那麼返回的是0的引用,直接++就變成了1,表示出現一次,後續在插入同樣的水果時,插入失敗,直接給value++,邏輯也沒有任何問題,因此這個方法最為簡單,推薦~
<code>string str[] = { "蘋果", "蘋果", "梨", "蘋果", "香蕉", "梨", "香蕉", "香蕉", "蘋果", "西瓜" };
map<string> count_map;
for (auto s : str)
{
\tcount_map[s]++;
}
for (const auto &e : count_map)
{
\tcout << e.first << ":" << e.second << endl;
}/<string>/<code>
三種方法都可以正確運行,結果如下:
<code>梨:2
蘋果:4
西瓜:1
香蕉:3/<code>
ref
https://blog.csdn.net/ETalien_/article/details/89442131
-End-
閱讀更多 小智雅匯 的文章