map 容器是鍵-值對的集合,好比以人名為鍵的地址和電話號碼。相反地,set 容器只是單純的鍵的集合。例如,某公司可能定義了一個名為 bad_checks的 set 容器,用於記錄曾經給本公司發空頭支票的客戶。當只想知道一個值是否存在時,使用 set 容器是最適合的。例如,在接收一張支票前,該公司可能想查詢 bad_checks 對象,看看該客戶的名字是否存在。
set 不支持下標操作符,而且沒有定義 mapped_type 類型。在 set 容器中,value_type 不是 pair 類型,而是與 key_type 相同的類型。它們指的都是 set 中存儲的元素類型。這一差別也體現了 set 存儲的元素僅僅是鍵,而沒有所關聯的值。與 map 一樣,set 容器存儲的鍵也必須唯一,而且
不能修改。
set 容器的定義和使用
為了使用 set 容器,必須包含 set 頭文件。
與 map 容器一樣,set 容器的每個鍵都只能對應一個元素。以一段范圍的元素初始化 set 對象,或在 set 對象中插入一組元素時,對於每個鍵,事實上都只添加了一個元素:
// define a vector with 20 elements, holding two copies of each number from 0 to 9
vector<int> ivec; for (vector<int>::size_type i = 0; i != 10; ++i) { ivec.push_back(i); ivec.push_back(i); // duplicate copies of each number
} // iset holds unique elements from ivec
set<int> iset(ivec.begin(), ivec.end()); cout << ivec.size() << endl; // prints 20
cout << iset.size() << endl; // prints 10
首先創建了一個名為 ivec 的 int 型 vector 容器,存儲 20 個元素:0-9(包括 9)中每個整數都出現了兩次。然后用 ivec 中所有的元素初始化一個int 型的 set 容器。則這個 set 容器僅有 10 個元素:ivec 中不相同的各個元素。
在 set 中添加元素
可使用 insert 操作在 set 中添加元素:
set<string> set1; // empty set
set1.insert("the"); // set1 now has one element
set1.insert("and"); // set1 now has two elements
另一種用法是,調用 insert 函數時,提供一對迭代器實參,插入其標記范圍內所有的元素。該版本的 insert 函數類似於形參為一對迭代器的構造函數——對於一個鍵,僅插入一個元素:
set<int> iset2; // empty set
iset2.insert(ivec.begin(), ivec.end()); // iset2 has 10 elements
與 map 容器的操作一樣,帶有一個鍵參數的 insert 版本返回 pair 類型對象, 包含一個迭代器和一個 bool 值, 迭代器指向擁有該鍵的元素, 而 bool 值表明是否添加了元素。使用迭代器對的 insert 版本返回 void 類型。
從 set 中獲取元素
set 容器不提供下標操作符。為了通過鍵從 set 中獲取元素,可使用 find運算。如果只需簡單地判斷某個元素是否存在,同樣可以使用 count 運算,返回 set 中該鍵對應的元素個數。當然,對於 set 容器,count 的返回值只能是1(該元素存在)或 0(該元素不存在):
iset.find(1) // returns iterator that refers to the element with key == 1
iset.find(11) // returns iterator == iset.end()
iset.count(1) // returns 1
iset.count(11) // returns 0
正如不能修改 map 中元素的鍵部分一樣,set 中的鍵也為 const。在獲得指向 set 中某元素的迭代器后,只能對其做讀操作,而不能做寫操作:
// set_it refers to the element with key == 1
set<int>::iterator set_it = iset.find(1); *set_it = 11; // error: keys in a set are read-only
cout << *set_it << endl; // ok: can read the key
使用實例
創建“單詞排除”集:
之前的程序使用從 map 對象 word_count 中刪除一個指定的單詞。可將這個操作擴展為刪除指定文件中所有的單詞(即該文件記錄的是排除集)。也即,我們的單詞統計程序只對那些不在排除集中的單詞進行統計。使用 set 和map 容器,可以簡單而直接地實現該功能:
void restricted_wc(ifstream &remove_file, map<string, int> &word_count) { set<string> excluded; // set to hold words we'll ignore
string remove_word; while (remove_file >> remove_word) excluded.insert(remove_word); // read input and keep a count for words that aren't in the
exclusion set
string word; while (cin >> word) // increment counter only if the word is not in excluded
if (!excluded.count(word)) ++word_count[word]; }
該函數首先讀取傳遞進來的文件,該文件列出了所有被排除的單詞。讀入這些單詞並存儲在一個名為 excluded 的 set 容器中。第一個 while 循環完成后,該 set 對象包含了輸入文件中的所有單詞。
接下來的程序類似原來的單詞統計程序。關鍵的區別在於:在統計每個單詞之前,先檢查該單詞是否出現在排除集中。第二個 while 循環里的 if 語句實現了該功能:
// increment counter only if the word is not in excluded
if (!excluded.count(word))
如果該單詞出現在排除集 excluded 中,則調用 count 將返回 1,否則返回 0。對 count 的返回值做“非”運算,則當該 word 不在 excluded 中時,條件測試成功,此時修改該單詞在 map 中對應的值。
與單詞統計程序原來的版本一樣,需要使用下標操作符的性質:如果某鍵尚未在map 容器中出現,則將該元素插入容器。所以語句;
++word_count[word];
的效果是:如果 word 還沒出現過,則將它插入到 word_count 中,並在插入元素后,將它關聯的值初始化為 0。然后不管是否插入了新元素,相應元素的值都加 1。