大一程設-下
東華大學
東華大學資管系
基本程式概念
資管經驗分享
參考文章請見最下面,這兩篇寫得非常好!!
上學期我們學過了最基礎的一般陣列,有特別強調說,他是在靜態時期編譯起來你就必須給他直接長度的,不能透過輸入賦予長度,雖然在某些 compiler 會過,但其實這件事情是不被大家所允許的。
接下來的 vector 就是動態的配置陣列大小給陣列,不用去操心記憶體,或是程式出錯,完全會自動配置大小,是非常好用的一個工具。
而 vector 是 array 的進階版,所以他還是一種 array,仍舊是連續配置記憶體,當容量不夠的時候會重新申請空間,並把原本的資料複製或搬到新的記憶體去。
但這樣有點不好,如果今天我們一直往 vector 裡面丟東西,一旦容量不夠,他可能會一直搬來搬去,其實有點沒有效率,因為他會一直釋放資源,添加資源,所以應該減少他配置新空間的次數。
上面這句話簡單的說就是,針對需求,可以配置比需求多一倍的空間,會是比較適當的。
要使用 vector,請務必要 #include <vector>
這個 Library 進來!
只是摘要,功能還有更多哦!
既然是陣列,他就是一種容器,空間就很重要,所以針對常用功能做摘要說明,下面會再細部的用例子帶大家理解。
Capacity,容量相關
size()
回傳陣列(vector)目前的長度empty()
看陣列是不是空的,如果 size 為 0 則回傳 true/1,反之則回傳 false/0reserve()
預先配置大小Modifiers,修飾器,針對放資料進陣列或從陣列移除資料
push_back()
放資料到 vector 的最後面pop_back()
從最後面刪除一個資料insert()
插入資料進 vectorerase()
從 vector 內移除資料clear()
清空 vectorIterators,疊代器,用來把指針指到我們要的位置
begin()
把疊代器指向第一個元素end()
把疊代器指向最後一個元素的下一個位置
Element Access,存取元素
at(i)
回傳參數索引值所在位置的值front()
回傳 vector 頭的值back()
回傳 vector 尾的值[i]
與 at 的用法相似,取得索引值位置的值vector<data_type> variable_name;
vector<int> arr_name;
vector 代表你要宣告一個 vector,<>符號裡面放的是你這個陣列要儲存的資料的型態,即每一格要放的資料型態,arr_name 就是為你的 vector 自訂一個名字。
你可以看到上面這樣的宣告,好像沒有告訴說這個陣列應該要存多少個資料,因為前面有講,vector 可以自動處理大小,所以可以不用宣告大小,但我會覺得養成宣告大小的習慣會好一些!
vector<int> arr_name(10);
//一維陣列,有十個儲存空間,每個空間都必須存整數,值預設每一個都會存 0
上面是只有宣告大小,那如果想要在宣告陣列即給值可以嗎? 當然可以!
vector<int> a({1,2,3,4,5,6,7,8});
//宣告一個名為 a 的 vector,其容量為 8
// a[0] = 1, a[1] = 2, a[2] = 3,...依此類推,大家都會
請注意,這樣寫就不用再宣告長度了,因為他就依據設置的值就可以知道容量多少,請記得 vector 可以動態配置大小。
今天這樣的一個 vector,可以怎麼取得長度呢?就是使用前面說的 size 成員函式。
vector<int> a({1,2,3,4,5,6,7,8});
cout << a.size(); // 當然會印出 8
在做值的初始化的時候,還有另一種寫法。
vector<int> a(5,100);
vector<string> b(10,"orange");
這個寫法是說,第一個參數代表陣列的 size,第二個參數是這個陣列一被宣告的時候,裡面的每一格初始化為什麼資料。所以 a 有五格,每一格都是 100。
但請注意,不要自作主張的寫成以下這樣。
vector<int> a(5,100,60,7,8,13);
不要覺得說第一個數字既然是 size,那我後面是不是可以直接寫每一格的內容是甚麼,沒有這種寫法,請千萬注意
在下方的參考文章內還有複製 vector 的寫法,但不是我覺得重要的點,有興趣歡迎去參考文章看看!
#include <vector>
using namespace std;
int main(){
vector<int>a; // 宣告一個空的陣列,目前大小空間為 0
a.push_back(5); // a 的 size 為 1
a.push_back(7); // a 的 size 為 2
a.push_back(3); // a 的 size 為 3
vector<string>b;
b.push_back("orange"); // b 的 size 為 1
b.push_back("apple"); // b 的 size 為 2
b.push_back("banana"); // b 的 size 為 3
a.pop_back(); // 放在 a 最後面的此時是 3,所以 3 被刪除
a.size(); // 此時 a 的 size 為 2
a.pop_back(); // 放在 a 最後面的此時是 7,所以 7 被刪除
a.size(); // 此時 a 的 size 為 1
return 0;
}
但如果你想要初始化陣列的時候就給予大小,你要注意下面這樣的寫法。
int main(){
vector<int> a(4);
a.push_back(3); // 此時 a[4] 也就是第五格,資料為 3
a.push_back(5); // 此時 a[5] 也就是第六格,資料為 5
a.push_back(9); // 此時 a[6] 也就是第七格,資料為 9
}
你可能會納悶為甚麼,因為 vector 在宣告即給大小的時候,他就配置的大小為 4 格的陣列出來,每一格都是 0,所以我們此時若 push_back,他會再加一個元素進來,成為第五位成員。
所以不要傻傻地覺得宣告沒有設值他會乖乖地從 0 開始哦,vector 不是這樣運作的。
上面的程式碼的話圖會像這樣。
所以如果想要動前四個的內容,就是使用我們學過的中括號摟!
a[0] = 5;
a[1] = 7;
a[2] = 11;
a[3] = 1;
到這邊你可能會覺得,只能夠從最後面增加跟刪除,好像有一點點沒有效率耶。
對!所以 vector 還有更進階的能夠直接插入或刪除中間元素的方式,我們後面來談。
但在談他們之前,我們要先來理解一下 vector iterator 中的 begin() 跟 end()。
不過 iterator (疊代器) 是比較進階的 vector,所以如果你想挑戰,歡迎去旁邊挑戰一下 Boss -> 我是旁邊的 Boss
比較不行的同學,你可以搞懂下面就好。
vector 的 capacity 是有必要好好談論的,他和 size 是完全不一樣的東西哦。
capacity 是取得目前 vector 配置的空間大小,而 size 是取得目前 vector 中有的元素個數。你可以想像成一個能夠裝 10 個東西的箱子(capacity),他目前裡面有 3 樣東西(size)。
而如果我們一直朝 vector 內放東西,放到滿了之後,因為 vector 會自動配置大小,所以他會擴充他的容量。而每次的擴充都會是當前 capacity 的 2 倍或 1.5 倍,取決於你的 compiler。
而容量會是 1、2、4、8、16、32,以 2 的冪次方成長下去。
int main(){
vector<double> a;
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
return 0;
}
所以上面的程式輸出如下,希望你能了解 capacity 跟 size 的差異。
reserve 函式能夠讓你告訴程式 vector 的 capacity 要多大。但裡面沒有放任何東西,所以 size 會是 0。
int main(){
vector<int> a;
a.reserve(10);
cout << "size:" << a.size() << ", capacity:" << a.capacity();
}
所以直到你 push_back 到他 size 要比 capacity 大的時候,他才會再擴充空間喔。照上面的說明來看,當我今天 size 要變成 11 的時候,capacity 就會變成 20 摟!
如果你今天是在宣告的時候就給予 vector 長度,像這樣 vector<int> a(10)
。則你的 size 跟 capacity 都會是 10 哦,原因是因為上面這樣的宣告方式,會把全部的格子初始化成 0,所以現在裡面的 10 格都放了 0。
但上面的 reserve 只有設定容器大小,還沒有在裡面放東西。
int main(){
vector<int> a(10);
cout << "size:" << a.size() << ", capacity:" << a.capacity();
}
希望大家不要搞混摟!
今天既然有配置容器大小,那有沒有把多餘用不到的刪除這件事呢? 當然也是有的,就是依靠 shrink_to_fit() 這個成員函式。
int main(){
vector<int> a;
a.reserve(10);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
a.shrink_to_fit();
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
}
有沒有發現他相較於陣列真的方便很多,雖然語法可能剛開始學會難一點點,但在一維陣列的處理上真的方便許多,雖然 vector 的二維寫起來比較困難一點就是了。
那今天有方法把多餘的刪除,那有沒有辦法在放了很多資料後,再把長度根據我們的需求改動呢? 一樣有的,就是依靠 resize() 成員函式。但他與 reserve 不同的是,多被配置出來的新空間,預設會補 0,至於你說 string、double 型態的 vector 預設會補上甚麼,大家可以自己測試。
int main(){
vector<int> a({1,2,3,4,5,6});
cout << "size:" << a.size() << "capacity:" << a.capacity() << endl;
//假設此時我們收到更進階的需求,知道需要 15 格。
a.resize(15);
cout << "size:" << a.size() << "capacity:" << a.capacity() << endl;
return 0;
}
vector 的基礎就先介紹到這邊啦,當然這些不是全部,但我相信也夠你們學了,有興趣的歡迎把 vector 比較進階的應用也學起來哦。
請照順序看!!