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; }需要在 try 程式碼塊中呼叫 map 的成員函數 at(),因為丟擲的任何未捕獲的異常都會導致程式的終止。這段程式碼獲取了 people 容器中的兩個物件,它們分別與兩個 Name 鍵關聯。如果 map 容器中的內容由執行的前一節中的程式碼段決定,輸出效果如下:
Dan Druff is aged 77
invalid map<K, T> key
Don Druff was not found.
auto age = people [Name {"Dan", "Druff”}];這裡獲取到一個和 Name 鍵關聯的 size_t 型別的值。注意,下標運算的使用並不是簡單的檢索機制。如果鍵不存在,元素預設的建構函式會用鍵和鍵所關聯的物件生成一個新元素,如果鍵關聯的物件是基本資料型別,它的值為 0。例如:
auto value = people[Name {"Ned", "Kelly"}]; // Creates a new element if the key is not there因為容器中不存在這個鍵,所以用它生成了新元素。關聯物件的值是 0,並會返回這個值。可以用下標運算子來更新 map 中的元素,如果元素不在 map 中,也可以用它插入元素。下標運算主要用在左賦值上,用來修改已存在的元素:
people[Name {"Ned", "Kelly”}] = 39; // Sets the value associated with the key to 39讓我們在新的範例中,用一種不同以往的方式使用 map,並且充分利用下標運算子。可以用 map 容器來確定每個字元在文字中出現的頻率。確定詞頻是非常有用的,例如,可以用它對文件進行分類。下面展示了如何在任意文字序列中統計每個單詞的出現次數:
// 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 中的文字是通過函數 getline() 得到的字串。replace_if() 演算法用空格替換了輸入中的所有非字母字元。replace_if() 函數的前兩個引數是定義元素範圍的疊代器,這裡的元素範圍就是輸入字串的字元。下一個引數是一個函數物件,當元素需要被替換時,它返回 true;這裡是一個 lambda 表示式。最後一個引數是用來替換的元素,在這個範例中這個元素是空格。這個函數會替換掉所有的標點,所以最後每個元素都是用空格分隔的。
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
#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這裡用 << 運算子來新增名言是合理的,它可以在其他一些場景下使用,例如輸入流。這裡也可以用 += 運算子來代替。這個類定義了 3 個版本的 operator<<(),提供了不同的方式去新增名言。第一個版本接收一個字串常數引數,然後把它傳給 vector 的成員函數 emplace_back(),emplace__back() 會呼叫 string 的建構函式以在適當的位置生成元素。第二個版本只有一個引數,它是 string 物件的參照,這個引數會被傳給 vector 的成員函數 push_back()。第三個版本有一個右值參照引數。當在函數體中通過名稱使用右值參照時,它會變成左值,因此必須使用 move() 函數將它變為右值,然後把它傳給 vector 的成員函數 push_back()。這會保證物件總是移動傳值,而不是複製傳值。
for (const auto& pr : quotations)//Requires const iterators ...可以在 main() 中定義兩個內聯輔助函數。第一個用來從 cin 讀入 name:
inline Name get_name() { Name name {}; std: :cout << "Enter first name and second name: "; std::cin >>std::ws >> name; return name; }這裡讀取的 name 用來作為名和姓。控制符 ws 用來消除空格,因此會跳過 cin 中剩下的字元。 第二個輔助函數用來讀取名言:
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; }可以輸入多行文字,然後用 * 號終止輸入。下面的程式支援儲存名言:
// 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; } } }quotations 容器儲存的是 pair<constName, Quotations> 物件型別的元素。像 quotations[name] 這種表示式可以參照 Name 物件 name 關聯的物件。如果在 map 中不存在和鍵值 name 關聯的 pair 物件,就用預設關聯的 Quotations 物件生成一個 pair 物件,預設的 Quotations 物件為空。下面的語句會為 name 儲存一條新的名言 quote:
quotations[name] << quote;<< 左邊的運算元等同於 quotations.operator[](name),它返回一個和 name 關聯的 Quotations 物件,因此這條語句等價於:
quotations.operator[](name).operator<<(quote);在 main() 函數中可以看到,我們利用表示式 quotations[name][index] 來得到一條名言,它等價於 quotations.operator[](name).operator[](index),你應該知道 main() 剩下的程式碼是如何工作的,下面就是一些範例輸出:
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
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;如果沒有和引數匹配的元素,find()函數會返回容器的結束疊代器,因此在使用這個疊代器之前,必須先對它進行檢查。