C++ map插入数据 图片看不了?点击切换HTTP 返回上层
1 2 3 4 | std::map<std:: string,size_t> people {std::make_pair ( "Ann" ,25),std::make_pair( "Bill" ,46) , std::make_pair ( "Jack" ,32), std::make_pair( "Jill" ,32)}; auto pr = std::make_pair( "Fred" ,22); // Create a pair element and insert it auto ret_pr = people. insert (pr); std::cout << ret_pr. first -> first << " " << ret_pr. first -> second << "" << std:: boolalpha <<ret_pr. second << "\n" ; // Fred 22 true |
1 | auto pr = std::make_pair<std:: string, size_t> (std:: string { "Fred" },22); |
1 | auto pr = std::make_pair ( "Fred" ,22) ; // pair<const char *, int > |
成员函数 insert() 会返回一个 pair<iterator,bool> 对象。对象的成员 first 是一个迭代器,它要么指向插入元素,要么指向阻止插入的元素。如果 map 中已经保存了一个和这个键相同的对象,就会出现后面这种情况。这个对象的成员变量 second (布尔型)是返回对象,如果插入成功,返回值为 true,否则为 false。
输出语句像我们看到的那样,访问插入 pair 的成员变量 first 的表达式是 ret_pr.first->first。ret_pr 的成员变量 first 是一个指向 pair 对象的迭代器,所以可以使用->操作符来访问它的成员变量 first。输出展示了插入的元素。可以通过下面这个循环进行验证:
1 2 | for (const auto& p : people) std::cout << std::setw(10) << std:: left << p. first << " " << p. second << "\n" ; |
Ann 25
Bill 46
Fred 22
Jack 32
Jill 32
通过执行下面这两条语句,可以看出元素插入后的效果:
1 2 | ret_pr = people. insert (std::make_pair( "Bill" , 48)); std:: cout << ret_pr. first -> first << " " <<ret_pr. first -> second << " " <<std::boolalpha<<ret_pr. second << "\n" ; // Bill 46 false |
当元素已经存在时,如果想将键值“Bill”对应的年龄值改为 48,可以像下面这样使用 insert() 返回的 pair 对象来做到这一点:
1 2 | if(!ret_pr. second ) // If the element is there change the age ret_pr. first —> second = 48; |
可以用 pair 构造函数生成的对象作为 insert() 的参数:
1 | ret_pr = people. insert (std::pair<const std::string, size_t> { "Bill" , 48}); |
也可以提供一个提示符来指出元素插入的位置。提示符是迭代器的形式,它指向容器中的一个现有元素,通常从提示符指示的位置开始查找新元素的插入位置。好的提示符可以提高插入操作的速度,反之亦然。例如:
1 2 | auto ret_pr = people. insert (std::make_pair( "Jim" , 48)); people. insert (ret_pr. first , std::make_pair ( "Ian" , 38)); |
下一个 insert() 函数的第一个参数和上面的提示符有关,所以这里就是插入元素的地方。insert() 的第二个参数指定的新元素会被插入到提示符的前面,并尽可能地靠近它。如果提示符不能以这种方式使用,那么将忽略它。同样地,如果被插入的元素已经在 map 中,会导致元素插入失败。带提示符的 insert() 调用会返回一个指向被插入元素或容器中阻止此插入操作的元素的迭代器,因此可以使用返回值来确定插入是否成功。当确定元素不存在时,可以只提供一个插入符,这是一个好的想法。如果不那么确定,而且仍然想使用插入符,map 中的 count() 函数对我们有一些帮助。它会返回 map 中指定键对应元素的数目,这个数目可能是 0 或 1。
因此代码可以这样写:
1 2 | if (!people. count ( "Ian" )) people. insert (ret_pr. first , std::make_pair ( "Ian" , 38)); |
也可以将外部源中的一段元素插入 map 中,这些元素不必来自另一个 map 容器,但必须和被插入容器中的元素是同类型。这里有一些示例:
1 2 3 4 | std::map<std::string, size_t> crowd {{ "May" , 55}, { "Pat" ,66}, { "Al" , 22}, { "Ben" , 44}}; auto iter = std:: begin (people); std::advance(iter, 4); // begin iterator+ 4 crowd. insert (++std:: begin (people),iter); // Insert 2nd, 3rd, and 4th elements from people |
下面是一个接受初始化列表作为参数的 insert() 版本:
1 | crowd. insert ({{ "Bert" , 44}, {"Ellen”, 99}}); |
1 2 | std::initializer_list<std::pair<const std:: string, size_t>>init {{ "Bert" , 44}, { "Ellen" , 99}}; crowd. insert (init); |
下面你会看到这些操作的一个完整示例。此处会定义一个有点不同的对象。Name 类型代表人名,这个类定义的头文件的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | // Defines a person's name #ifndef NAME_H #define NAME_H #include <string> // For string class #include <ostream> // For output streams #include <istream> // For input streams class Name { private: std::string first {}; std::string second {}; public : Name (const std::string& name1, const std::string& name2) : first (name1), second (name2) {} Name () = default ; // Less-than operator bool operator<(const Name & name ) const { return second < name . second || ( second == name . second && first < name . first ); } friend std::istream& operator>>(std::istream& in , Name & name ); friend std::ostream& operator<<(std::ostream& out , const Name & name ); }; // Extraction operator overload inline std::istream& operator>>(std::istream& in , Name & name ) { in >> name . first >> name . second ; return in ; } // Insertion operator overload inline std::ostream& operator<<(std::ostream& out , const Name & name ) { out << name . first + " " + name . second ; return out ; } #endif |
map 容器中的元素是 std::pair<const Name,size_t> 类型,我们可以用下面定义的别名来简化代码:
1 | using Entry = std::pair<const Name , size_t>; |
1 2 3 4 5 6 7 8 | Entry get_entry() { std::cout << "Enter first and second names followed by the age: " ; Name name {}; size_t age {}; std::cin >> name >> age; return make_pair( name , age); } |
可以输出容器元素的辅助函数也是很有用的:
1 2 3 4 5 6 7 | void list_entries(const map< Name , size_t>& people) { for (auto& entry : people) { std::cout << std:: left << std::setw(30) << entry. first << std:: right << std::setw(4) << entry. second << std::endl; } } |
包含 main() 函数的源文件中的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | // Storing names and ages #include <iostream> // For standard streams #include <iomanip> // For stream manipulators #include <string> // For string class #include <map> // For map container class #include <utility> // For pair<> & make_pair<>() #include <cctype> // For toupper() #include "Name.h" using std::string; using Entry = std::pair<const Name , size_t>; using std::make_pair; using std::map; // Create a map entry from input Entry get_entry() { std::cout << "Enter first and second names followed by the age: " ; Name name {}; size_t age {}; std::cin >> name >> age; return make_pair( name , age); } // Output the elements in a map void list_entries(const map< Name , size_t>& people) { for (auto& entry : people) { std::cout << std:: left << std::setw(30) << entry. first << std:: right << std::setw(4) << entry. second << std::endl; } } int main() { map< Name , size_t> people {{{ "Ann" , "Dante" }, 25}, {{ "Bill" , "Hook" }, 46}, {{ "Jim" , "Jams" }, 32}, {{ "Mark" , "Time" }, 32}}; std::cout << "\nThe initial contents of the map is:\n" ; list_entries(people); char answer { 'Y' }; std::cout << "\nEnter a Name and age entry.\n" ; while(std::toupper(answer) == 'Y' ) { Entry entry {get_entry()}; auto pr = people. insert (entry); if(!pr. second ) { // It 's there already - check whether we should update std::cout << "Key \"" << pr.first->first << "\" already present. Do you want to update the age (Y or N)? "; std::cin >> answer; if(std::toupper(answer) == ' Y') pr. first -> second = entry. second ; } // Check whether there are more to be entered std::cout << "Do you want to enter another entry(Y or N)? " ; std::cin >> answer; } std::cout << "\nThe map now contains the following entries:\n" ; list_entries(people); } |
map 容器的定义中有一些初始值,它们是初始化列表中的元素。这里只是为了说明如何在这种情况下使用嵌套花括号。在定义元素的每个初始化列表中,花括号中的 Name 对象都是初始化列表,每个初始化元素的花括号中都是一个 Name 对象和一个年龄值。最外面的一对花括号包括了所有元素的初始值。
辅助函数 list_entries() 用来显示容器的初始状态。从 for 循环中读入了更多的 entry 对象。循环由 answer 的值控制,如果一开始它的值就是 'Y',那么最少执行一次循环,最少从键盘输入一个元素。entry 对象的类型是 Entry 它也是容器元素的类型。辅助函数 get_entry() 的返回对象被作为 entry 的初始值。然后将它作为 insert() 的参数插入到容器中。这个函数返回的 pair 对象有一个成员变量 first,它指向容器中和 entry 的键匹配的元素。如果在插入之前,容器中就存在这个元素,那么它指向的是原始容器中的元素。如果键已经存在于容器中,插入不会成功,并且 pr 的成员变量 second 为 false。
pr.first 是容器中元素的一个迭代器,因此 pr.first->second 可以访问与键关联的对象,如果用户确认需要更新,pr.first->second 的值会变为 entry.second。循环中的最后一个操作可以决定是否输入更多的 entry。在不需要输入更多的 entry 时,循环结束,用 list_entries() 函数输出这个容器最终的内容。
下面是这个示例的一些输出结果:
The initial contents of the map is:
Ann Dante 25
Bill Hook 46
Jim Jams 32
Mark Time 32
Enter a Name and age entry.
Enter first and second names followed by the age: Emma Nate 42
Do you want to enter another entry(Y or N)? y
Enter first and second names followed by the age: Emma Nate 43
Key "Emma Nate" already present. Do you want to update the age (Y or N)? Y
Do you want to enter another entry(Y or N)? y
Enter first and second names followed by the age: Eamonn Target 56
Do you want to enter another entry(Y or N)? n
The map now contains the following entries:
Ann Dante 25
Bill Hook 46
Jim Jams 32
Emma Nate 43
Eamonn Target 56
Mark Time 32