1. 程式人生 > >STL原始碼剖析(五)關聯式容器--【set、multiset、map、multimap】

STL原始碼剖析(五)關聯式容器--【set、multiset、map、multimap】

文章目錄

1. 寫在前面

  • 在前面我們對於RB-tree的設計與實現都有了一定的瞭解,那麼這一節中所要解析的set以及map看起來就簡單了許多,因為這兩者都是以RB-tree作為其底層資料結構,大部分操作直接呼叫RB-tree的操作實現的,在這裡就不再重複提及了,只針對set及map的性質及特有操作進行解析

2. set

2.1 set性質

1. set以RB-tree作為其底層機制
2. 所有元素都會根據元素的鍵值自動被排序
3. set的元素就是鍵值,set不允許兩個元素有相同的鍵值
4. 不允許通過set的迭代器來改變set的元素值因為set的元素值就是鍵值,更改了元素值就會影響其排列規則,如果任意更改元素值,會嚴重破壞set組織,因此在定義set的迭代器時被定義成了RB-tree的const_iterator
5. 由於set不允許有兩個相同的鍵值,所以插入時採用的是RB-tree的insert_unique方式

2.2 set實現

  • 以下只將set特有的一些性質通過其源碼錶現出來
template <class Key, class Compare = less<key>, class Alloc = allloc>
class set {
public:
	typedef key key_type;      //鍵值與實值相同
	typedef key value_type;
	typedef Compare key_compare;
	typedef Compare value_compare;
private:
	//...
	typedef rb_tree<key_type, value_type, identify<value_type>
, key_compare, Alloc> rep_type; rep_type t; //採用紅黑樹來表現set public: //... typedef typename rep_type::const_iterator iterator; //定義為const_iterator,不允許更改 //... //以下列舉出的建構函式與插入均使用insert_unique方式 template <class InputIterator> set(InputIterator first, InputIterator lst) : t(Compare()) { t.insert_unique(first, last); } //... iterator insert(iterator position, const value_type& x) { //其中一個插入操作版本 typedef typename rep_type::iterator rep_iterator; return t.insert_unique((rep_iterator&)position, x); } //... void erase(iterator position) { //其中一個版本的刪除操作 typedef typename rep_type::iterator rep_iterator; t.erase((rep_iterator&)position); } //... //在first和last的前閉後開的區間中進行二分查詢第一個不小於x的值 iterator lower_bound(const key_type& x) const { return t.low_bound(x); } //在first和last的前閉後開的區間中進行二分查詢第一個大於x的值 iterator upper_bound(const key_type& x) const { return t.upper_bound(x); } //返回上述兩種方式返回的迭代器區間 pair<iterator, iterator> equal_range(const key_type& x) const { return t.equal_range(x); } //... };

2.3 multiset

  • 與set特性完全相同,唯一差別在於它允許鍵值重複,因此插入操作採用的是底層機制RB-tree的insert_equal()而非insert_unique()
template < class Key, class Compare = less<Key>, class Alloc = alloc>
class multiset {
public:
//...
	template <class InputIterator>
	multiset(InputIterator first, InputInterator last)
		: t(Compare()) { t.insert_equal(first, last); }
		//...
};

3. map

3.1 map性質

1. map採用RB-tree作為其底層機制
2. 所有元素都會根據元素的鍵值自動被排序
3. map的所有元素都是pair,同時擁有實值和鍵值,pair的第一元素被視為鍵值,第二元素被視為元素值,map不允許兩個元素擁有相同的鍵值
4. 不能通過map的迭代器改變map的鍵值,因為map元素的鍵值關係到map元素的排列規則,任意改變map的元素鍵值會破壞map組織;但可以修正元素的實值
5. 由於map不允許有兩個相同的鍵值,所以插入時採用的是RB-tree的insert_unique方式

3.2 pair

  • pair是一個結構體型別,裡面成員都是public的:
template <class T1, class T2>
struct pair {
	typedef T1 first_type;
	typedef T2 second_type;
	T1 first;
	T2 second;
	pair() : first(T1()), second(T2()) {}
	pair(const T1& a, const T2& b) : first(a), second(b)  {}
};

3.3 map實現

  • 以下只將map特有的一些性質通過其源碼錶現出來:
template <class Key, class T,  class Compare = less<key>, class Alloc = allloc>
class map{
public:
	typedef key key_type;      //鍵值與實值相同
	typedef T data_type;      //資料型別
	typedef T mapped_type;
	typedef pair<const Key, T> value_type;   //元素型別(鍵值/實值)
	typedef Compare key_compare;   //鍵值比較函式

	//呼叫元素比較函式
	class value_compare
	    : public binary_function<value_type, value_type, bool> {
	friend class map<Key, T, Compare, Alloc>;
	protected:
		Compare comp;
		value_compare(Compare c) : comp(c) {}
	public:
		bool operator() (const value_type& x, const value_type& y) const {
			return comp(x.first, y.first);
		}
	};
private:
	//...
	typedef rb_tree<key_type, value_type, select1st<value_type>, 
		key_compare, Alloc> rep_type;
	rep_type t;      //採用紅黑樹來表示map
public:
	//...
	typedef typename  rep_type::iterator iterator;  //定義為iterator,允許更改

	//...
	//以下列舉出的建構函式與插入均使用insert_unique方式
	template <class InputIterator>
	map(InputIterator first, InputIterator lst)
		: t(Compare()) { t.insert_unique(first, last); }
	//...
	value_compare value_comp() const { return value_compare(t.key_compare());  } //元素比較
	//...
	T& operator[] (const key_type& k)  {   //下標操作符
		return (*((insert(value_type(k, T()))).first)).second; 
	}
	
	iterator insert(iterator position, const value_type& x) {  //其中一個插入操作版本
		return t.insert_unique(position, x);
	}
	pair<iterator, bool> insert(const value_type& x)  { //另一個插入操作版本
		return t.insert_unique(x); }
	//...
	void erase(iterator position) {     //其中一個版本的刪除操作
		typedef typename rep_type::iterator rep_iterator;
		t.erase((rep_iterator&)position);
	}
	//...
	//在first和last的前閉後開的區間中進行二分查詢第一個不小於x的值
	 iterator lower_bound(const key_type& x) const {
	 	return t.low_bound(x);   
	 }
	 //在first和last的前閉後開的區間中進行二分查詢第一個大於x的值
	 iterator upper_bound(const key_type& x) const {
	 	return t.upper_bound(x);
	 }
	 //返回上述兩種方式返回的迭代器區間
	 pair<iterator, iterator> equal_range(const key_type& x) const {
	 	return t.equal_range(x);
	 }
	 //...
};

3.4 insert()函式及subscript()操作

  1. insert()函式:針對引數只有一個元素值的插入函式:
pair<iterator, bool> insert(const value_type& x)
	{ return t.insert_unique(x); }

我們可以觀察上面的函式,其返回值是一個pair型別,其第一個值為迭代器型別,第二個值為bool型別,其中bool型別代表此次插入是否成功,而迭代器型別則表示指向被插入的那個元素

  1. subscript()操作:

用法有兩種:

  • 作為左值運用:
map<string, int> simap;
simap[string("jjhou")] = 1;    //左值運用
  • 作為右值運用:
int number = simap[string("jjhou")];  //右值引用

分析下標操作函式:

T& operator[] (const key_type& k)  {
	return (*((insert(value_type(k, T()))).first)).second; 
}
  1. 首先產生一個臨時物件:
value_type(k, T())
  1. 再將該元素插入到map中:
insert(value_type(k, T()))
  1. 取插入操作返回的pair的第一元素:
(insert(value_type(k, T()))).first
  1. 第一元素是一個迭代器,提領迭代器:
*((insert(value_type(k, T()))).first)
  1. 獲得一個map元素,取其第二元素:
(*((insert(value_type(k, T()))).first)).second

3.5 multimap

  • 與map特性完全相同,唯一差別在於它允許鍵值重複,因此插入操作採用的是底層機制RB-tree的insert_equal()而非insert_unique()
template < class Key, class Compare = less<Key>, class Alloc = alloc>
class multimap {
public:
//...
	template <class InputIterator>
	multiset(InputIterator first, InputInterator last)
		: t(Compare()) { t.insert_equal(first, last); }
		//...
};