(未完成)STL学习笔记(3)序列式容器 (Sequence Containers)

1. vector

vector与数组array十分相似,但array是静态空间,而vector是动态空间,可以通过内部机制自行扩充空间,具有很好的灵活性。
其实现的关键在于对大小的控制和重新配置时的数据移动效率。
vector的型别定义如下:

template <class T, class Alloc = alloc>
class vector{
// ...
public:
	typedef T            value_type;
	typedef value_type*  pointer;
	typedef value_type*  iterator;
	typedef value_type&  reference;
	typedef size_t       size_type;
	typedef ptrdiff_t    difference_type;
// ...
}

1.1 vector 的数据结构


很简单,就是线性连续空间,使用了三个迭代器来指示这段空间的范围

template <class T, class Alloc = alloc>
class vector{
// ...
protected:
	iterator start;             //表示目前使用空间的头
	iterator finish;            //表示目前使用空间的尾
	iterator end_of_storage;    //表示目前可用空间的尾
// ...
}

vector实际的配置大小一般会比需求量更大一些,我们用容量(capacity)表示vector当前的容量,而用大小(size)表示已经使用的空间大小。

template <class T, class Alloc = alloc>
class vector{
// ...
public:
	iterator begin() { return start; }
	iterator end() { return finish; }
	size_type size() const { return size_type(end() - begin()); }
	size_type capacity() const{ return size_type(end_of_storage - begin()); }
	bool empty() const { return begin() == end(); }
	reference operator[](size_type n) { return *(begin() + n); }

	reference front() { return *begin(); }
	reference back() { return *(end() - 1); }
// ...
}

1.2 vector的构造与内存管理: constructor, push_back

vector缺省使用第二章所介绍的alloc作为空间配置器,并用其定义了data_allocator进行配置。

template <class T, class Alloc = alloc>
class vector{
protected:
	typedef simple_alloc<value_type, Alloc> data_allocator;
	//于是 data_allocator::allocate(n)表示配置n个元素空间
	// ...
	//构造函数,允许vector大小n和初值value
	vector<size_type n,const T& value){ fill_initialize(n, value); }
	
	//填充并初始化
	void fill_initialize(size_type n, const T& value){
		start = allocate_and_fill(n, value);
		finish = start + n;
		end_of_storage = finish;
	}
	//配置后再填充
	iterator allocate_and_fill(size_type n, const T& x){
		iterator result = data_allocator::allocate(n);
		//根据result的性别特性决定使用fill_n()或者反复调用construct()完成
		uninitialized_fill_n(result, n, x);
		return result;
	}

当我们使用push_back()将新元素插入尾端时,必须先检查是否还有备用空间,若没有则必须扩充空间(重新配置、移动数据、释放原空间),一般是配置原空间大小的两倍的新空间,此时指向vector的所有迭代器都失效。

template <class T, class Alloc = alloc>
class vector{
public:
  // 增加一個元素,做為最後元素
 	void push_back(const T& x) {
    	if (finish != end_of_storage) {  // 還有備用空間
     		construct(finish, x);   		// 直接在備用空間中建構元素。
      		++finish;                          	// 調整水位高度
    	}
    	else                                  // 已無備用空間
      		insert_aux(end(), x);			
	}
	//在position插入x
	template <class T, class Alloc>
	void vector<T, Alloc>::insert_aux(iterator position, const T& x) {
		if (finish != end_of_storage) {  // 還有備用空間
	    	// 在備用空間起始處建構一個元素,並以vector 最後一個元素值為其初值。 
	    	construct(finish, *(finish - 1));
	    	// 調整水位。
	    	++finish;
	    	T x_copy = x;
	    	copy_backward(position, finish - 2, finish - 1);
	    	*position = x_copy;
		}
		else {		// 已無備用空間
			const size_type old_size = size();
	    	const size_type len = old_size != 0 ? 2 * old_size : 1;
	    	// 以上配置原則:如果原大小為0,則配置 1(個元素大小);
	    	// 如果原大小不為0,則配置原大小的兩倍,
	    	// 前半段用來放置原資料,後半段準備用來放置新資料。
	
	    	iterator new_start = data_allocator::allocate(len); // 實際配置
	    	iterator new_finish = new_start;
			try {
	      		// 將原vector 的內容拷貝到新 vector。
	      		new_finish = uninitialized_copy(start, position, new_start);
	      		// 為新元素設定初值x
	      		construct(new_finish, x);
	      		// 調整水位。
	      		++new_finish;
	      		// 将插入点到尾端的内容也考过了(因为insert也会调用insert_aux)
	      		new_finish = uninitialized_copy(position, finish, new_finish);
	    	}
	    	catch(...) {
	      		// "commit or rollback" 語意:若非全部成功,就一個不留。
	      		destroy(new_start, new_finish); 
	      		data_allocator::deallocate(new_start, len);
	      		throw;
	    	}
	    	// 解構並釋放原 vector
	    	destroy(begin(), end());
	    	deallocate();
	
	    	// 調整迭代器,指向新vector
	    	start = new_start;
	    	finish = new_finish;
	    	end_of_storage = new_start + len;
	 	}
	}
}

1.3 vectord的元素操作(pop_back, erase, clear, insert)

1.3.1 pop_back()

void pop_back() {
    --finish;			//尾端向前一格表示放弃最后一格
    destroy(finish);	// 全域函式,建構/解構基本工具。
  }

1.3.2 erase()

//清除某个位置上的元素
iterator erase(iterator position) {
    if (position + 1 != end()) // 如果 p 不是指向最後一個元素
      // 將 p 之後的元素一一向前遞移
      copy(position + 1, finish, position); 
    --finish;  // 調整水位
    destroy(finish);	// 全域函式,建構/解構基本工具。
    return position;
  }
  
 //清除[first, last)上的元素
iterator erase(iterator first, iterator last) {
	iterator i = copy(last, finish, first);//将last及之后元素拷贝至first
    destroy(i, finish);	// 全域函式,建構/解構基本工具。
    finish = finish - (last - first);
    return first;
  }

1.3.3 insert()

// 從 position 開始,安插 n 個元素,元素初值為 x
template <class T, class Alloc>
void vector<T, Alloc>::insert(iterator position, size_type n, const T& x) {
	if (n != 0) { // 當 n != 0 才進行以下所有動作
    	if (size_type(end_of_storage - finish) >= n) { 
      		// 備用空間大於等於「新增元素個數」
      		T x_copy = x;
      		// 以下計算安插點之後的現有元素個數
      		const size_type elems_after = finish - position;	
      		iterator old_finish = finish;
      		if (elems_after > n) { 
        		// 「安插點之後的現有元素個數」大於「新增元素個數」
        		uninitialized_copy(finish - n, finish, finish);
        		finish += n;	// 將vector 尾端標記後移
        		copy_backward(position, old_finish - n, old_finish);
        		fill(position, position + n, x_copy);	// 從安插點開始填入新值
      		}
      		else {	
        		// 「安插點之後的現有元素個數」小於等於「新增元素個數」
        		uninitialized_fill_n(finish, n - elems_after, x_copy);
        		finish += n - elems_after;
        		uninitialized_copy(position, old_finish, finish);
        		finish += elems_after;
        		fill(position, old_finish, x_copy);
       		}
		}
    	else {
    		// 備用空間小於「新增元素個數」(那就必須配置額外的内存)
      		// 首先決定新長度:舊長度的兩倍,或舊長度+新增元素個數。
      		const size_type old_size = size();        
      		const size_type len = old_size + max(old_size, n);
      		// 以下配置新的vector 空間
      		iterator new_start = data_allocator::allocate(len);
      		iterator new_finish = new_start;
      		__STL_TRY {
        		// 以下首先將舊vector 的安插點之前的元素複製到新空間。
        		new_finish = uninitialized_copy(start, position, new_start);
        		// 以下再將新增元素(初值皆為 n)填入新空間。
        		new_finish = uninitialized_fill_n(new_finish, n, x);
        		// 以下再將舊vector 的安插點之後的元素複製到新空間。
        		new_finish = uninitialized_copy(position, finish, new_finish);
      		}
	# ifdef __STL_USE_EXCEPTIONS 
      		catch(...) {
        		// 如有異常發生,實現 "commit or rollback" semantics.
        		destroy(new_start, new_finish);
        		data_allocator::deallocate(new_start, len);
        		throw;
      		}
# endif /* __STL_USE_EXCEPTIONS */
      		// 以下清除並釋放舊的 vector 
      		destroy(start, finish);
      		deallocate();
      		// 以下調整水位標記
      		start = new_start;
      		finish = new_finish;
      		end_of_storage = new_start + len;
    	}
  	}
}
  • 情况一:备用空间充足,且elems_after>n
    n=2; elems_after=2; 备用空间end_of_storage-finish=2;

  • 情况二:备用空间充足,且elems_after<=n
    n=4; elems_after=2; 备用空间end_of_storage-finish=3;

  • 情况三:备用空间不足容纳新增元素
    n=3; 备用空间end_of_storage-finish=2;

2 list

list实质上是一个双向链表,需要提供Bidirectional Iterators。
与vector不同,list的节点并不能保证存储于连续空间,但它每插入或删除一个元素就配置或释放一个元素空间,不造成空间浪费,同时插入和删除过程也是常数空间。

2.1 list的节点(node)

template<class T>
struct __list_node{
	typedef void* void_pointer;
	void_pointer prev;
	void_pointer next;
	T data;
}

2.2 list的迭代器

之前说过list的迭代器是Bidirectional Iterators,且需要提供递增、递减、取值、成员调用等操作。
同时,与vector更不同的是insert和splice等操作并不会导致list迭代器失效,而erase也只会导致指向被删除节点的迭代器失效。

// 串列專屬迭代器。既然撰寫串列迭代器避免不了要曝露串列的實作細節,
// 那麼就讓串列和串列迭代器一起設計好了。
template<class T, class Ref, class Ptr>
struct __list_iterator { // 未繼承 std::iterator
  typedef __list_iterator<T, T&, T*>             iterator;
  typedef __list_iterator<T, const T&, const T*> const_iterator;
  typedef __list_iterator<T, Ref, Ptr>           self;

  // 未繼承 std::iterator,所以必須自行撰寫五個必要的迭代器相應型別
  typedef bidirectional_iterator_tag iterator_category;	 // (1)
  typedef T value_type; 			// (2)
  typedef Ptr pointer; 			// (3)
  typedef Ref reference; 			// (4)
  typedef __list_node<T>* link_type;
  typedef size_t size_type;
  typedef ptrdiff_t difference_type; // (5)

  link_type node;  // 保持與容器的聯結,指向list的节点

  // 构造函数
  __list_iterator(link_type x) : node(x) {}
  __list_iterator() {}
  __list_iterator(const iterator& x) : node(x.node) {}

  // 迭代器必要的操作行為
  bool operator==(const self& x) const { return node == x.node; }
  bool operator!=(const self& x) const { return node != x.node; }
  // 關鍵:對迭代器取值(dereference),取的是節點的数据值。
  reference operator*() const { return (*node).data; }	

#ifndef __SGI_STL_NO_ARROW_OPERATOR
//成员存取运算子的标准做法
  pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */

  // 參考 More Effective C++, item6: Distinguish between prefix and
  // postfix forms of increment and decrement operators.
  // 關鍵:對迭代器累加1,就是前進一個節點
  self& operator++() { 
    node = (link_type)((*node).next);  	
    return *this;
  }
  self operator++(int) { 
    self tmp = *this;
    ++*this;
    return tmp;
  }
  // 對迭代器累減1,就是後退一個節點
  self& operator--() { 
    node = (link_type)((*node).prev); 	// 關鍵
    return *this;
  }
  self operator--(int) { 
    self tmp = *this;
    --*this;
    return tmp;
  }
};

2.3 list的数据结构

list是一个双向链表,仅需要一个纸箱尾端的空白节点就能表现整个链表,且满足STL的“前闭后开”区间要求。

template<class T, class Alloc = alloc>
class list {
protected:
	typedef __list_node<T> list_node;
	typedef __list_iterator<T, T&, T*>             iterator;
  	typedef __list_iterator<T, const T&, const T*> const_iterator;
public:
	typedef list_node* link_type;
	iterator begin() { return (link_type)((*node).next); }
  	const_iterator begin() const { return (link_type)((*node).next); }
  	// node 指向尾節點的下一位置,因此 node 符合STL對 end 的定義。
  	iterator end() { return node; }	
    const_iterator end() const { return node; }
    
  	reverse_iterator rbegin() { return reverse_iterator(end()); }
  	const_reverse_iterator rbegin() const { 
    	return const_reverse_iterator(end()); 
  	}
  	reverse_iterator rend() { return reverse_iterator(begin()); }
  	const_reverse_iterator rend() const { 
  		return const_reverse_iterator(begin());
  	} 
  	
  	bool empty() const { return node->next == node; }
  	size_type size() const {
    	size_type result = 0;
    	distance(begin(), end(), result);  // 全域函式,定義於 <stl_iterator.h>
    	return result;
  	}
	 size_type max_size() const { return size_type(-1); }


  	// 取頭節點的內容(元素值)。
  	reference front() { return *begin(); }  
  	const_reference front() const { return *begin(); }
 	// 取尾節點的內容(元素值)。
  	reference back() { return *(--end()); } 
  	const_reference back() const { return *(--end()); }

protected:
	link_type node;
//...
}

2.4list的构造与内存管理(constructor,push_back,insert)

list使用省却alloc作为空间配置器,定义list_node_allocator以节点为单位进行配置,使得list_node_allocator(n)表示配置n个节点空间,使得配置 释放 构造 销毁更简易。

template <class T, class Alloc = alloc>
class list {
public:
	list(){ empty_initialize(); }//产生空链表
protected:
	typedef __list_node<T> list_node;
	//以节点为单位进行配置
	typedef simple_alloc<list_node, Alloc> list_node_allocator;
	
	//配置 释放 构造 销毁
	// 配置一個節點並傳回
  	link_type get_node() { return list_node_allocator::allocate(); }
  	// 釋放一個節點
  	void put_node(link_type p) { list_node_allocator::deallocate(p); }

  	// 產生(配置並建構)一個節點,帶有元素值
 	link_type create_node(const T& x) {
    	link_type p = get_node();
    	__STL_TRY {
      		construct(&p->data, x);	// 全域函式,建構/解構基本工具。
    	}
    	__STL_UNWIND(put_node(p));
    	return p;
  	}
  	// 摧毀(解構並釋放)一個節點
  	void destroy_node(link_type p) {
    	destroy(&p->data); 		// 全域函式,建構/解構基本工具。
    	put_node(p);
  	}

全部评论

相关推荐

投递腾讯云智研发等公司7个岗位
点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务