C++ map获取元素 图片看不了?点击切换HTTP 返回上层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Name key ; try { key = Name { "Dan”, ”Druff" }; auto value = people. at ( key ); std:: cout << key << "is aged " << value << std:: endl; key = Name { "Don" , "Druff" }; value = people. at ( key ); std::cout << key << " is aged " << value << std::endl; } catch(const std::out_of_range& e) { std::cerr << e.what() << '\n' << key << " was not found." <<std::endl; } |
Dan Druff is aged 77
invalid map<K, T> key
Don Druff was not found.
map 容器提供了以键为参数的下标运算符,它可以返回一个和键所关联对象的引用。下面是一个示例:
1 | auto age = people [ Name { "Dan" , "Druff”}]; |
1 | auto value = people[ Name { "Ned" , "Kelly" }]; // Creates a new element if the key is not there |
1 | people[ Name { "Ned" , "Kelly”}] = 39; // Sets the value associated with the key to 39 |
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 | // Determining word frequency #include <iostream> // For standard streams #include <iomanip> // For stream manipulators #include <string> // For string class #include <sstream> // For istringstream #include <algorithm> // For replace_if() & for_each() #include <map> // For map container #include <cctype> // For isalpha() using std::string; int main() { std::cout << "Enter some text and enter * to end:\n" ; string text_in {}; std::getline(std::cin, text_in, '*' ); // Replace non-alphabetic characters by a space std::replace_if(std:: begin (text_in), std:: end (text_in), [](const char & ch){ return !isalpha(ch); }, ' ' ); std::istringstream text(text_in); // Text input string as a stream std::istream_iterator<string> begin (text); // Stream iterator std::istream_iterator<string> end ; // End stream iterator std::map<string, size_t> words; // Map to store words & word counts size_t max_len {}; // Maximum word length // Get the words, store in the map, and find maximum length std::for_each( begin , end , [&max_len, &words](const string& word) { words[word]++; max_len = std:: max (max_len, word.length()); }); size_t per_line {4}, count {}; for (const auto& w : words) { std::cout << std:: left << std::setw(max_len + 1) << w. first << std::setw(3) << std:: right << w. second << " " ; if(++ count % per_line == 0) std::cout << std::endl; } std::cout << std::endl; } |
我们用 text_in 生成一个 istringstream 对象 text。istringstream 对象允许对它封装的字符串进行流输入操作,因此可以把它当作一个流。这也包括从 text 获得流迭代器的能力,然后可以在 for_each() 中用它们提取单个单词。输入流的迭代器会陆续指向每个输入的字符串。这里输入的单词是连续的,因此开始和结束迭代器指定的范围是 text 中的所有单词。
for_each() 会将第 3 个参数指定的函数对象运用到前两个参数所指定范围内的元素上。函数对象必须以迭代器指向对象类型的引用作为参数,所以这里参数是 const string &。lambda 以引用的方式捕获变量 max_len 和 words,所以它们都可以修改。lambda 通过将每个单词作为下标来将它们以键的方式保存在容器中,并增加单词关联的值。如果单词不在容器中,会以这个单词为键(值为 1)来生成一个新的元素。如果单词先前就被添加到容器中,就自动增加值。因此与每个单词的关联值就是它在文本中累计出现的次数。为了保存最长字符串的长度,lambda 表达式也会更新 max_len。后面的输出中会用到这个值。
因而调用 for_each() 会将输入的所有单词都插入到这个 map 容器中,并且累加计算出每个单词的出现次数,计算出最大单词的长度,一条语句就实现了上面这些功能。
下面是程序输出的结果:
Enter some text and enter * to end:
How much wood would a wood chuck chuck,
If a woodchuck could chuck wood?
A woodchuck would chuck as much wood as a woodchuck could chuck if a woodchuck could chuck wood.
*
A 1 How 1 If 1 a 4
as 2 chuck 6 could 3 if 1
much 2 wood 5 woodchuck 4 would 2
假设我们要通过人名来保存并检索名人名言。显然,一个名人会有很多名言,因此我们需要通过单个键来保存多个名言。我们不能在 map 容器中保存重复的键,但是可以将键关联到封装了多个名言的对象上。我们可以用前面章节中的Name类作为键,然后定义 Quotations 类用来保存指定名人的所有名言。
我们知道,可以用键的下标运算符来访问和键关联的对象,因此可以通过扩展 Quotations 类的成员函数 operator[]() 来实现这个功能。为了方便向 Quotation 类中添加名言,我们还在类中实现了 operator<<0。我们可以方便地将名言保存在 vector 容器中。下面就是定义了这个类的 Quotations.h 头文件的内容:
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 63 64 65 66 67 68 69 70 71 72 | #ifndef QUOTATIONS_H #define QUOTATIONS_H #include <vector> // For vector container #include <string> // For string class #include <exception> // For out_of_range exception class Quotations { private: std::vector<std::string> quotes; // Container for the quotations public : // Stores a new quotation that is created from a string literal Quotations& operator<<(const char * quote) { quotes.emplace_back(quote); return *this; } // Copies a new quotation in the vector from a string object Quotations& operator<<(const std::string& quote) { quotes.push_back(quote); return *this; } // Moves a quotation into the vector Quotations& operator<<(std::string&& quote) { quotes.push_back(std:: move (quote)); return *this; } // Returns a quotation for an index std::string& operator[](size_t index ) { if( index < quotes. size ()) return quotes[ index ]; else throw std::out_of_range { "Invalid index to quotations." }; } size_t size () const// Returns the number of quotations { return quotes. size (); } // Returns the begin iterator for the quotations std::vector<std::string>::iterator begin () { return std:: begin (quotes); } // Returns the const begin iterator for the quotations std::vector<std::string>::const_iterator begin () const { return std:: begin (quotes); } // Returns the end iterator for the quotations std::vector<std::string>::iterator end () { return std:: end (quotes); } // Returns the const end iterator for the quotations std::vector<std::string>::const_iterator end () const { return std:: end (quotes); } }; #endif |
类的成员函数 []() 可以通过索引来访问成员元素。当索引不在范围内时,这个函数将抛出一个异常,这种情况不应该发生;如果真的发生,这会是程序中的一个 bug。
在 vector 容器中,begin() 和 end() 返回指向名言的迭代器。需要注意的是,返回类型是指定的。提供迭代器的容器通常会定义一个迭代器成员变量,作为它们支持的迭代器类型的别名,所以不需要知道类型的具体细节。类对象定义的迭代器可以结合 for 循环使用,但要求迭代器至少是正向迭代器。
在 Quotations 类中也定义了 const 版本的 begin() 和 end(),它们的返回值都是 const 类型的迭代器。这个返回类型有一个别名,定义在 vector 模板中。如果没有定义 const 版的 begin() 和 end() 函数,就不能在 for 循环中使用 const 类型的循环变量,例如:
1 2 | for (const auto& pr : quotations)//Requires const iterators ... |
1 2 3 4 5 6 7 | inline Name get_name() { Name name {}; std: :cout << "Enter first name and second name: " ; std::cin >>std::ws >> name ; return name ; } |
1 2 3 4 5 6 7 | inline string get_quote(const Name & name ) { std::cout << "Enter the quotation for " << name << ".Enter * to end: \n" ; string quote; std::getline(std::cin >> std::ws, quote, '*' ); return quote; } |
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | // Stores one or more quotations for a name in a map #include <iostream> // For standard streams #include <cctype> // For toupper() #include <map> // For map containers #include <string> // For string class #include "Quotations.h" #include "Name.h" using std::string; // Read a name from standard input inline Name get_name() { Name name {}; std::cout << "Enter first name and second name: " ; std::cin >> std::ws >> name ; return name ; } // Read a quotation from standard input inline string get_quote(const Name & name ) { std::cout << "Enter the quotation for " << name << ". Enter * to end:\n" ; string quote; std::getline(std::cin >> std::ws, quote, '*' ); return quote; } int main() { std::map< Name , Quotations> quotations; // Container for name /quotes pairs std::cout << "Enter 'A' to add a quote." "\nEnter 'L' to list all quotes." "\nEnter 'G' to get a quote." "\nEnter 'Q' to end.\n" ; Name name {}; // Stores a name string quote {}; // Stores a quotation char command {}; // Stores a command while(command != 'Q' ) { std::cout << "\nEnter command: " ; std::cin >> command; command = static_cast< char >(std::toupper(command)); switch(command) { case 'Q' : break; // Quit operations case 'A' : name = get_name(); quote = get_quote( name ); quotations[ name ] << quote; break; case 'G' : { name = get_name(); const auto& quotes = quotations[ name ]; size_t count = quotes. size (); if(! count ) { std::cout << "There are no quotes recorded for " << name << std::endl; continue ; } size_t index {}; if( count > 1) { std::cout << "There are " << count << " quotes for " << name << ".\n" << "Enter an index from 0 to " << count - 1 << ": " ; std::cin >> index ; } std::cout << quotations[ name ][ index ] << std::endl; } break; case 'L' : if(quotations.empty()) // Test for no pairs { std::cout << "\nNo quotations recorded for anyone." << std::endl; } // List all quotations for (const auto& pr : quotations) // Iterate over pairs { std::cout << '\n' << pr. first << std::endl; for (const auto& quote : pr. second ) // Iterate over quotations { std::cout << " " << quote << std::endl; } } break; default : std::cout << " Command must be 'A', 'G', 'L', or 'Q'. Try again.\n" ; continue ; break; } } } |
1 | quotations[ name ] << quote; |
1 | quotations.operator[]( name ).operator<<(quote); |
Enter 'A' to add a quote.
Enter 'L' to list all quotes.
Enter 'G' to get a quote.
Enter 'Q' to end.
Enter command: a
Enter first name and second name: Winston Churchill
Enter the quotation for Winston Churchill. Enter * to end:
There are a terrible lot of lies going around the world, and the worst of it is half of them are true.*
Enter command: a
Enter first name and second name: Dorothy Parker
Enter the quotation for Dorothy Parker. Enter * to end:
Beauty is only skin deep,but ugly goes clean to the bone.*
Enter command: a
Enter first name and second name: Winston Churchill
Enter the quotation for Winston Churchill. Enter * to end:
Never in the field of human conflict was so much owed by so many to so few.*
Enter command: a
Enter first name and second name: Winston Churchill
Enter the quotation for Winston Churchill. Enter * to end:
Courage is what it takes to stand up and speakm,Courage is also what it takes to sit down and listen.*
Enter command: a
Enter first name and second name: Dorothy Parker
Enter the quotation for Dorothy Parker. Enter * to end:
Money cannot buy health,but I'd settle for a diamond-studded wheelchair.*
Enter command: g
Enter first name and second name: Winston Churchill
There are 3 quotes for Winston Churchill.
Enter an index from 0 to 2: 1
Never in the field of human conflict was so much owed by so many to so few.
Enter command: L
Winston Churchill
There are a terrible lot of lies going around the world, and the worst of it is half of them are true.
Never in the field of human conflict was so much owed by so many to so few.
Courage is what it takes to stand up and speakm,Courage is also what it takes to sit down and listen.
Dorothy Parker
Beauty is only skin deep,but ugly goes clean to the bone.
Money cannot buy health,but I'd settle for a diamond-studded wheelchair.
Enter command: q
map 容器的成员函数 fmd() 可以返回一个元素的迭代器,这个元素的键值和参数匹配。例如:
1 2 3 4 5 6 7 | std::map<std::string, size_t> people {{ "Fred" , 45}, { "Joan" , 33}, { "Jill" , 22}}; std::string name { "Joan" }; auto iter = people.find( name ); if(iter == std:: end (people)) std:: cout << "Not found.\n" ; else std:: cout << name << " is " "<< iter-> second << std::endl; |
为了兼容 multimap,map 容器包含了成员函数 equal_range(}、upper_bound()和 lower_ bound(),因为这些函数会用来查找具有相同键的多个元素。稍后在 multimap 容器这一节中对它们进行深入讲解。