講解有4種方式C++ STL map insert()插入資料
前面講過,C++STLmap 類模板中對[ ]
運算子進行了過載,即根據使用場景的不同,藉助[ ]
運算子可以實現不同的操作。舉個例子:
#include <iostream> #include <map> //map #include <string> //string using namespace std; int main() { std::map<string, string> mymap{ {"STL教程","http:///java/"} }; //獲取已儲存鍵值對中,指定鍵對應的值 cout << mymap["STL教程"] << endl; //向 map 容器新增新鍵值對 mymap["Python教程"] = "http:///python/"; //修改 map 容器已儲存鍵值對中,指定鍵對應的值 mymap["STL教程"] = "http:///stl/"; for (auto iter = mymap.begin(); iter != mymap.end(); ++iter) { cout << iter->first << " " << iter->second << endl; } return 0; }
程式執行結果為:
http:///java/ Python教程 http:///python/ STL教程 http:///stl/
可以看到,當操作物件為 map 容器中已儲存的鍵值對時,則藉助 [ ] 運算子,既可以獲取指定鍵對應的值,還能對指定鍵對應的值進行修改;反之,若 map 容器內部沒有儲存以 [ ] 運算子內指定資料為鍵的鍵值對,則使用 [ ] 運算子會向當前 map 容器中新增一個新的鍵值對。
實際上,除了使用 [ ] 運算子實現向 map 容器中新增新鍵值對外,map 類模板中還提供有insert()成員方法,該方法專門用來向 map 容器中插入新的鍵值對。
注意,這裡所謂的“插入”,指的是 insert() 方法可以將新的鍵值對插入到 map 容器中的指定位置,但這與 map 容器會自動對儲存的鍵值對進行排序並不衝突。當使用 insert() 方法向 map 容器的指定位置插入新鍵值對時,其底層會先將新鍵值對插入到容器的指定位置,如果其破壞了 map 容器的有序性,該容器會對新鍵值對的位置進行調整。
自 C++ 11 標準後,insert() 成員方法的用法大致有以下 4 種。
1) 無需指定插入位置,直接將鍵值對新增到 map 容器中。insert() 方法的語法格式有以下 2 種:
//1、引用傳遞一個鍵值對 pair<iterator,bool> insert (const value_type& val); //2、以右值引用的方式傳遞鍵值對 template <class P> pair<iterator,bool> insert (P&& val);
其中,val 引數表示鍵值對變數,同時該方法會返回一個 pair 物件,其中 pair.first 表示一個迭代器,pair.second 為一個 bool 型別變數:
- 如果成功插入 val,則該迭代器指向新插入的 val,bool 值為 true;
- 如果插入 val 失敗,則表明當前 map 容器中存有和 val 的鍵相同的鍵值對(用 p 表示),此時返回的迭代器指向 p,bool 值為 false。
以上 2 種語法格式的區別在於傳遞引數的方式不同,即無論是區域性定義的鍵值對變數還是全域性定義的鍵值對變數,都採用普通引用傳遞的方式;而對於臨時的鍵值對變數,則以右值引用的方式傳參。有關右值引用,可閱讀《C++右值引用》一文做詳細瞭解。
舉個例子:
#include <iostream> #include <map> //map #include <string> //string using namespace std; int main() { //建立一個空 map 容器 std::map<string, string> mymap; //建立一個真實存在的鍵值對變數 std::pair<string, string> STL = { "STL教程","http:///stl/" }; //建立一個接收 insert() 方法返回值的 pair 物件 std::pair<std::map<string, string>::iterator, bool> ret; //插入 STL,由於 STL 並不是臨時變數,因此會以第一種方式傳參 ret = mymap.insert(STL); cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl; //以右值引用的方式傳遞臨時的鍵值對變數 ret = mymap.insert({ "C語言教程","/c/" }); cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl; //插入失敗樣例 ret = mymap.insert({ "STL教程","/java/" }); cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl; return 0; }
程式執行結果為:
ret.iter = <{STL教程, http:///stl/}, 1> ret.iter = <{C語言教程, http:///c/}, 1> ret.iter = <{STL教程, http:///stl/}, 0>
從執行結果中不難看出,程式中共執行了 3 次插入操作,其中成功了 2 次,失敗了 1 次:
- 對於插入成功的 insert() 方法,其返回的 pair 物件中包含一個指向新插入鍵值對的迭代器和值為 1 的 bool 變數
- 對於插入失敗的 insert() 方法,同樣會返回一個 pair 物件,其中包含一個指向 map 容器中鍵為 "STL教程" 的鍵值對和值為 0 的 bool 變數。
另外,在程式中的第 21 行程式碼,還可以使用如下 2 種方式建立臨時的鍵值對變數,它們是等價的:
//呼叫 pair 類模板的建構函式 ret = mymap.insert(pair<string,string>{ "C語言教程","http:///c/" }); //呼叫 make_pair() 函式 ret = mymap.insert(make_pair("C語言教程", "http:///c/"));
2) 除此之外,insert() 方法還支援向 map 容器的指定位置插入新鍵值對,該方法的語法格式如下:
//以普通引用的方式傳遞 val 引數 iterator insert (const_iterator position, const value_type& val); //以右值引用的方式傳遞 val 鍵值對引數 template <class P> iterator insert (const_iterator position, P&& val);
其中 val 為要插入的鍵值對變數。注意,和第 1 種方式的語法格式不同,這裡 insert() 方法返回的是迭代器,而不再是 pair 物件:
- 如果插入成功,insert() 方法會返回一個指向 map 容器中已插入鍵值對的迭代器;
- 如果插入失敗,insert() 方法同樣會返回一個迭代器,該迭代器指向 map 容器中和 val 具有相同鍵的那個鍵值對。
舉個例子:
#include <iostream> #include <map> //map #include <string> //string using namespace std; int main() { //建立一個空 map 容器 std::map<string, string> mymap; //建立一個真實存在的鍵值對變數 std::pair<string, string> STL = { "STL教程","http:///stl/" }; //指定要插入的位置 std::map<string, string>::iterator it = mymap.begin(); //向 it 位置以普通引用的方式插入 STL auto iter1 = mymap.insert(it, STL); cout << iter1->first << " " << iter1->second << endl; //向 it 位置以右值引用的方式插入臨時鍵值對 auto iter2 = mymap.insert(it, std::pair<string, string>("C語言教程", "http:///c/")); cout << iter2->first << " " << iter2->second << endl; //插入失敗樣例 auto iter3 = mymap.insert(it, std::pair<string, string>("STL教程", "http:///java/")); cout << iter3->first << " " << iter3->second << endl; return 0; }
程式執行結果為:
STL教程 http:///stl/ C語言教程 http:///c/ STL教程 http:///stl/
再次強調,即便指定了新鍵值對的插入位置,map 容器仍會對儲存的鍵值對進行排序。也可以說,決定新插入鍵值對位於 map 容器中位置的,不是 insert() 方法中傳入的迭代器,而是新鍵值對中鍵的值。
3) insert() 方法還支援向當前 map 容器中插入其它 map 容器指定區域內的所有鍵值對,該方法的語法格式如下:
template <class InputIterator> void insert (InputIterator first, InputIterator last);
其中 first 和 last 都是迭代器,它們的組合<first,last>
可以表示某 map 容器中的指定區域。
舉個例子:
#include <iostream> #include <map> //map #include <string> //string using namespace std; int main() { //建立並初始化 map 容器 std::map<std::string, std::string>mymap{ {"STL教程","/stl/"}, {"C語言教程","http:///c/"}, {"Java教程","http:///java/"} }; //建立一個空 map 容器 std::map<std::string, std::string>copymap; //指定插入區域 std::map<string, string>::iterator first = ++mymap.begin(); std::map<string, string>::iterator last = mymap.end(); //將<first,last>區域內的鍵值對插入到 copymap 中 copymap.insert(first, last); //遍歷輸出 copymap 容器中的鍵值對 for (auto iter = copymap.begin(); iter != copymap.end(); ++iter) { cout << iter->first << " " << iter->second << endl; } return 0; }
程式執行結果為:
Java教程 http:///java/ STL教程 http:///stl/
此程式中,<first,last> 指定的區域是從 mumap 容器第 2 個鍵值對開始,之後所有的鍵值對,所以 copymap 容器中包含有 2 個鍵值對。
4) 除了以上一種格式外,insert() 方法還允許一次向 map 容器中插入多個鍵值對,其語法格式為:
void insert ({val1, val2, ...});
其中,vali都表示的是鍵值對變數。
舉個例子:
#include <iostream> #include <map> //map #include <string> //string using namespace std; int main() { //建立空的 map 容器 std::map<std::string, std::string>mymap; //向 mymap 容器中新增 3 個鍵值對 mymap.insert({ {"STL教程", "http:///stl/"}, { "C語言教程","http:///c/" }, { "Java教程","http:///java/" } }); for (auto iter = mymap.begin(); iter != mymap.end(); ++iter) { cout << iter->first << " " << iter->second << endl; } return 0; }
程式執行結果為:
C語言教程 http:///c/ Java教程 http:///java/ STL教程 http:///stl/
值得一提的是,除了 insert() 方法,map 類模板還提供 emplace() 和 emplace_hint() 方法,它們也可以完成向 map 容器中插入鍵值對的操作,且效率還會 insert() 方法高。關於這 2 個方法,會在下一節做詳細介紹。