c++ --- vector

一.vector的介紹和使用

  • (1)vector是可變大小數組的序列容器。
  • (2)就像數組一樣,vector也採用連續存儲空間來儲存元素。也就意味着可以採用下標對vector的元素進行訪問,和數組一樣高效。但是又不像數組,它的大小是可以動態改變的,而且它的大小會被容器自動處理。
  • (3)本質上講,vector使用動態分配數組來存儲它的元素。當新元素插入的時候,這個數組需要被重新分配大小,爲了增加存儲空間。其做法是,分配一個新的數組,然後講全部的元素移到這個數組中。就是賤而言,這是一個相對代價高的任務。因爲當時每當一個新的元素加入到容器的時候,vector並不會每次都重新分配大小。
  • (4)vector分配空間策略:vector會分配一些額外的空間以適應可能的增長,因爲存儲空間比實際需要的存儲空間更大。不同的庫採用不同的策略權衡空間的使用和重新分配。但是無論如何,重新分配都應該是 對數增長的間隔大小,以至於在末尾插入一個元素的時候是在常數時間的複雜度完成的。
  • (5)因此,vector佔用了更多的存儲空間,爲了獲得管理存儲空間的能力,並且以一種有效的方式動態增長。
  • (6)與其他動態序列容器相比(deques,lists and forward_list), vector在訪問元素的時候更加高效,在末尾添加和刪除元素相對高效。對於其他不在末尾的刪除和插入操作,效率更低。比起list和forward_lists統一的迭代器和引用更好。

1.vector的定義

構造函數聲明 接口說明
vector() 無參構造
vector(size_type n, const value_type& val = value_type()) 構造並初始化n個val
vector (const vector& x); 拷貝構造
vector (InputIterator first, InputIterator last); 使用迭代器進行初始化構造

2.vector iterator的使用

iterator的使用 接口說明
begin() 獲取第一個數據位置的iterator
end() 獲取最後一個數據的下一個位置的iterator
rbegin() 獲取最後一個數據位置的reverse_iterator
rend() 獲取第一個數據前一個位置的reverse_iterator
cbegin() 獲取第一個數據位置的const_iterator
cend() 獲取最後一個數據的下一個位置的const_iterator

3.vector的空間增長問題

容量空間 接口說明
size() 獲取數據個數
capacity() 獲取容量大小
empty() 判斷是否爲空
void resize (size_type n, value_type val = value_type()); 改變vector的size
void reserve (size_type n); 改變vector放入capacity

  • (1)capacity的代碼在vs和g++下分別運行會發現,vs下capacity是按1.5倍增長的,g++是按2倍增長的。 這個問題經常會考察,不要固化的認爲,順序表增容都是2倍,具體增長多少是根據具體的需求定義 的。vs是PJ版本STL,g++是SGI版本STL。
  • (2) reserve只負責開闢空間,如果確定知道需要用多少空間,reserve可以緩解vector增容的代價缺陷問 題。
  • (3)resize在開空間的同時還會進行初始化,影響size

4.vector增刪查改

vector增刪查改 接口說明
void push_back (const value_type& val); 尾插
void pop_back(); 尾刪
InputIterator find (InputIterator first, InputIterator last, const T& val);
查找。(注意這個是算法模塊實現,不是 vector的成員接口)
iterator insert (iterator position, const value_type& val);在position之前插入val
iterator erase (iterator position); 刪除position位置的數據
void swap (vector& x); 交換兩個vector的數據空間
reference operator[] (size_type n); 像數組一樣訪問

二.vector的迭代器失效問題

當你使用vector容器時,如果你進行下面2種中的任意一種操作:

  • 1.vector已滿再進行插入操作
  • 2.使用erase()刪除數據.

就會導致迭代器失效.
舉個例子:

#include<iostream>
#include<vector>
#include<list>
#include<string>
using namespace std;

int main()
{	
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	//給vector中插入6個數據.	
	
	auto e = v.begin();
	v.insert(v.end(), 7);//再用insert
	
	while (e != v.end())
	{
		cout << *e<<" ";
		e++;
	}
	
	system("pause");
	return 0;
}

比如上面的程序,給數組v插入6個數據的時候,在插入第七個數據後,用迭代器遍歷v就會出現錯誤。當vector被填滿時,在要插入7時。就要對vector進行增容。而我們都知道,vector擴容的原理是:開闢一塊更大的空間,將原空間數據騰到新的空間,在釋放掉原來的空間,在釋放空間的過程中,迭代器始終都沒有發生變化,這就意味着,擴容完畢之後,迭代器仍然指向着你釋放的空間。此時,在對迭代器進行++操作就會引發錯誤。

解決辦法:在擴容之後重新定位迭代器,就不會有問題了。
第二種情況是進行erase()操作

#include<iostream>
#include<vector>
#include<list>
#include<string>
using namespace std;

int main()
{	
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	
	vector<int>::iterator it = find(v.begin(), v.end(), 3);//返回3位置的迭代器
	v.insert(it, 30);//3前面插入30
	it = v.begin();

	while (it != v.end())//刪掉所有的偶數
	{
		if (*it % 2 == 0)
		{
			v.erase(it);
		}
		it++;
	}
	auto e = v.begin();
	while (e != v.end())
	{
		cout << *e<<" ";
	}
	
	system("pause");
	return 0;
}

由上述的代碼,我們可以得出結論,在刪除偶數的過程中3被跳過了,沒有檢查是否爲偶數,這是因爲在erase的底層實現是後面的元素向前挪覆蓋掉被刪除元素所導致的,當我們以爲迭代器正常迭代的時候,其實它已經失效了,最可怕的是,當最後一個元素4被刪除檢查的時候,迭代器還有進行++操作,因爲邏輯上此時它還不是end(),但是end()其實前挪了,所以迭代器跑到了vector的外面,導致出現錯誤。

解決辦法:
erase(iterator it)函數的返回值是被刪除的下一個有效位置的迭代器,所以我們將這個值賦給迭代器就不會出現問題。

	while (it != v.end())//刪掉所有的偶數
	{
		if (*it % 2 == 0)
		{
			it = v.erase(it);
		}
		else
		{
			it++;
		}
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章