1. 程式人生 > 其它 >c++ vector 複製_C++:vector小指南(附帶一些新手錯誤)

c++ vector 複製_C++:vector小指南(附帶一些新手錯誤)

技術標籤:c++ vector 複製

參考文章:

1.C++_vector操作_劉同學的部落格-CSDN部落格_vector

2.C++ vector 容器淺析 | 菜鳥教程

1. vector

  • vector是向量型別,可以容納許多型別的資料,因此也被稱為容器
  • (可以理解為動態陣列,是封裝好了的類)
  • 進行vector操作前應新增標頭檔案#include <vector>

1.1 vector 建立:

一維(不指定列長):

vector<int>a;

二維(指定行數,固定列長):

int N = 5, M = 6;
vector<vector<int>>obj(N, vector<int>(M)); //定義二維動態陣列5行6列

輸出結果(預設填充0):

690b0387277fe7cb3c812349fcd4d89d.png

二維(指定行數,不固定列長):

int N = 5, M = 6;
vector<vector<int> > obj(N); //定義二維動態陣列大小5行 
for (int i = 0; i < obj.size(); i++)//動態二維陣列為5行(i+3)列,值全為0 
{
	obj[i].resize(i+3);
}

輸出結果(預設填充0):

987bd0e8cf422fbc9d35e21276b5d212.png

1.2 一維vector初始化:

方式1.

//定義具有10個整型元素的向量(尖括號為元素型別名,它可以是任何合法的資料型別),不具有初值,其值不確定
vector<int>a(10);

3b657e6d53bd5d257d982f5a26b28a23.png

方式2.

//定義具有10個整型元素的向量,且給出的每個元素初值為1
vector<int>a(10,1);

6b6bdb914c759a186db2a14ade62898a.png

方式3.

//用向量b給向量a賦值,a的值完全等價於b的值
vector<int>a(b);

方式4.

//將向量b中從0-2(共三個)的元素賦值給a,a的型別為int型
vector<int>a(b.begin(),b.begin+3);

方式5.

//從陣列中獲得初值
int b[7]={1,2,3,4,5,6,7};
vector<int> a(b,b+7);

1.3 vector物件的常用內建函式使用(舉例說明)

#include<vector>
vector<int> a,b;
//b為向量,將b的0-2個元素賦值給向量a
a.assign(b.begin(),b.begin()+3);

//a含有4個值為2的元素
a.assign(4,2);

//返回a的最後一個元素
a.back();

//返回a的第一個元素
a.front();

//返回a的第i元素,當且僅當a存在
a[i];

//清空a中的元素
a.clear();

//判斷a是否為空,空則返回true,非空則返回false
a.empty();

//刪除a向量的最後一個元素
a.pop_back();

//刪除a中第一個(從第0個算起)到第二個元素,也就是說刪除的元素從a.begin()+1算起(包括它)一直到a.begin()+3(不包括它)結束
a.erase(a.begin()+1,a.begin()+3);

//在a的最後一個向量後插入一個元素,其值為5
a.push_back(5);

//在a的第一個元素(從第0個算起)位置插入數值5,
a.insert(a.begin()+1,5);

//在a的第一個元素(從第0個算起)位置插入3個數,其值都為5
a.insert(a.begin()+1,3,5);

//b為陣列,在a的第一個元素(從第0個元素算起)的位置插入b的第三個元素到第5個元素(不包括b+6)
a.insert(a.begin()+1,b+3,b+6);

//返回a中元素的個數
a.size();

//返回a在記憶體中總共可以容納的元素個數
a.capacity();

//將a的現有元素個數調整至10個,多則刪,少則補,其值隨機
a.resize(10);

//將a的現有元素個數調整至10個,多則刪,少則補,其值為2
a.resize(10,2);

//將a的容量擴充至100,
a.reserve(100);

//b為向量,將a中的元素和b中的元素整體交換
a.swap(b);

//b為向量,向量的比較操作還有 != >= > <= <
a==b;

2. 順序訪問vector的幾種方式,舉例說明

2.1. 對向量a新增元素的幾種方式

(1)向向量a中新增元素

vector<int>a;
for(int i=0;i<10;++i){a.push_back(i);}

(2)從陣列中選擇元素向向量中新增

int a[6]={1,2,3,4,5,6};
vector<int> b;
for(int i=0;i<=4;++i){b.push_back(a[i]);}

(3)從現有向量中選擇元素向向量中新增

int a[6]={1,2,3,4,5,6};
vector<int>b;
vector<int>c(a,a+4);
for(vector<int>::iterator it=c.begin();it<c.end();++it)
{
	b.push_back(*it);
}

(4)從檔案中讀取元素向向量中新增

ifstream in("data.txt");
vector<int>a;
for(int i;in>>i){a.push_back(i);}

2.2 從向量中讀取元素

(1)通過下標方式獲取(元素必須存在!)

int a[6]={1,2,3,4,5,6};
vector<int>b(a,a+4);
for(int i=0;i<=b.size()-1;++i){cout<<b[i]<<endl;}

(2)通過迭代器方式讀取

int a[6]={1,2,3,4,5,6};
 vector<int>b(a,a+4);
 for(vector<int>::iterator it=b.begin();it!=b.end();it++){cout<<*it<<"  ";}

3.幾個常用的演算法

#include<algorithm>
 //對a中的從a.begin()(包括它)到a.end()(不包括它)的元素進行從小到大排列
 sort(a.begin(),a.end());

 //對a中的從a.begin()(包括它)到a.end()(不包括它)的元素倒置,但不排列,如a中元素為1,3,2,4,倒置後為4,2,3,1
 reverse(a.begin(),a.end());
 
 //把a中的從a.begin()(包括它)到a.end()(不包括它)的元素複製到b中,從b.begin()+1的位置(包括它)開始複製,覆蓋掉原有元素
 copy(a.begin(),a.end(),b.begin()+1);
 
//在a中的從a.begin()(包括它)到a.end()(不包括它)的元素中查詢10,若存在返回其在向量中的位置
  find(a.begin(),a.end(),10);

4.常見錯誤總結(都是我跳過的坑..)

(1)在建立vector時,若沒有指定列長,則向量中不存在任何元素,無法被讀取。如:

vector<int>a;
for (int i = 0; i < a.size(); i++)
{
	cout << a[i] << " ";
}

結果:程式或許可以正常執行,但是不會輸出任何內容。

(2)只有vector某位置已經存在元素時,才可以用下標去賦值/改值。如:

vector<int>a;
for (int i = 0; i < a.size(); i++)
{
	a[i] = i + 2;
	cout << a[i];
}

結果:程式或許可以正常執行,但是不會輸出任何內容。

(3)出現警告 warning C4018: “<”: 有符號/無符號不匹配。如:

a23017ada9b4352d909883e472c18826.png

以下程式碼將被警告:

vector<int>a(10,1);
for (int i = 0; i < a.size(); i++)
{
	cout << a[i] << " ";
}

警告原因:

a 是一個vector容器,a .size() 在容器說明中被定義為: unsigned int 型別, 而 iint 型別,所以會出現: 有符號/無符號不匹配警告。

也就是:在 比較運算子 前後 的 數值型別 要相同,問題可能在左側,也可能在右側,具體情況具體分析!

修改方法:

將 i 改為 unsigned int 型別即可。

(4)在自定義類資料成員中新增 vector 時,出現錯誤:“應輸入型別說明符”。如:

fb1d1f9be720957e490206edf63f8c09.png

問題分析:

①vector是封裝的類,當在一個類中定義另一個類時,就會出現這樣的報錯;

②問題實質在於,當定義 vector 的時候同時呼叫了 vector 的帶參建構函式。我們可以將“N”放到類的建構函式中去。如:

class Init_Population
{
private:
	int N = 10;
	vector<vector<int>>generate_population;
public:
        //建構函式
	Init_Population()
	{
		generate_population.resize(N);
	}
};

參考:C++ vector 實現二維陣列時, 在類的標頭檔案中定義時遇到"應輸入型別符"的問題?

③其他解決方式

一般在類外(主函式、任何函式中)定義vector時,都可以“直接初始化”,即定義的同時就指定行寬列長等。

既然在類中定義vector不可以“直接初始化”,那麼就使用“拷貝式初始化”。如:

//改成花括號
int N = 10;
vector<vector<int>>generate_population{ N };

參考:為何在類中初始化另一個類會出錯? - 知乎 https://www.zhihu.com/question/329974084

(5)一個關於類和物件的小插曲,寫在這是因為起初以為是vector無法被return,最後發現不是vector的問題。問題如下:

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

class Init_a
{
private:
	int i, j;
	int N = 10;
	vector<int>a;
public:
	//給向量 a 賦值
	void SetValue()
	{
		for (i = 0; i < N; i++)
		{
			a.push_back(i + 1);
		}
	}
	//獲取向量 a 的資料
	int GetValue(int k)
	{
		return a[k];
	}
};
int main()
{
	int N = 10;
	Init_a a1;
	a1.SetValue();

	//正確!(物件一致,才有功能上的延續性)
	for (int i = 0; i < N; i++)
	{
		cout << a1.GetValue(i) << " ";
	}
	
	錯誤!(物件不一致,功能上不具有延續性)
	//Init_a a2;
	//for (int i = 0; i < N; i++)
	//{
	//	cout << a2.GetValue(i) << " ";
	//}

	return 0;
	system("pause");
}

引以為戒:程式碼幹太久,容易變傻。。這種小毛病還真不太好發現!

(6)錯誤 "xxxx":C++提示非標準語法;請使用 "&" 來建立指向成員的指標

修改

在提示的那一行認真檢查類成員函式的呼叫,如

Widget.name
//Widget.name(); 正確的呼叫方式

原因

此時使用的應該是類的成員函式,而不是類的成員變數當我們使用類的成員函式時,VS編輯器並不會自動幫我們加上圓括號(它會提示是成員函式,還是成員變數哦[圖示顏色不同]),所以很多新手會忘記加()以表示函式的呼叫。當不加括號是,也就是函式不傳入引數(void也是引數哦)的話,編譯器將會理解成函式指標(在c++中,函式名就是函式的起始地址),但函式指標需要這樣寫

&Widget.name

也是個小馬虎導致的問題,隨便記錄一下。。

(7)容器(vector)作為函式引數如何傳參

參考:c++ 函式vector傳參_h799710的部落格-CSDN部落格

之前的一些函式使用陣列傳參的,用了vector後,傳參出錯,需要用vector的傳參方式了。

vector傳參的三種方式:

  • void func1(vector vet); //傳送數值
  • void func2(vector &vet); //引用
  • void func3(vector *vet); //指標

這三種方式產生的效果:

  • 呼叫拷貝建構函式,形參改變不會影響到實參
  • 不呼叫拷貝構函式,形參改變影響到實參
  • 不呼叫拷貝構函式,形參改變影響到實參

程式碼如下:

void func1(vector<int> vet) {
	vet.emplace_back(1);
}
void func2(vector<int>  &vet) {
	vet.emplace_back(2);
}
void func3(vector<int>  *vet) {
	vet->emplace_back(3);	//指標用 ->
}
int main() {
	vector<int> vet;
	func1(vet);
	cout << vet[0] << endl;	//!!!報錯,因為實參不會改變(想正常執行可註釋掉這句)

	vector<int> vet2;
	func2(vet2);
	cout << vet2[0] << endl;	//輸出 2

	vector<int> vet3;
	func3(&vet3);	//這裡取得是指標
	cout << vet3[0] << endl;	//輸出 3
	//system("pause");
	return 0;
}

小結:

  • vector最好採用引用或者指標傳參,因為如果資料量大的時候,拷貝會非常的慢;如果只是不想讓資料被修改,可以在vector前加上const
  • 之前用陣列喜歡用指標傳參,但是在vector中不是那麼方便,vector指標傳參後,每一次呼叫值都需要加上取地址符&,有點麻煩。用引用傳參好一點。