Array
array是c++ 11新增的序列容器,和其他容器的區別是,array的大小是固定的,無法動態擴展或者收縮。這也就意味着,在使用該容器的過程無法借由增加或移除元素而改變其大小,它只允許訪問或者替換存儲的元素。在使用該容器之前,代碼中需引入 <array>
頭文件
初始化
#include <array>
std::array<double, 10> values;
由此,就創建好了一個名為 values 的 array 容器,其包含 10 個浮點型元素。但是,由於未顯式指定這 10 個元素的值,因此使用這種方式創建的容器中,各個元素的值是不確定的(array 容器不會做默認初始化操作)。
通過如下創建 array 容器的方式,可以將所有的元素初始化為 0 或者和默認元素類型等效的值:
std::array<double, 10> values {};
當然,在創建 array 容器的實例時,也可以像創建常規數組那樣對元素進行初始化:
std::array<double, 10> values {0.5,1.0,1.5,2.0};
可以看到,這里只初始化了前 4 個元素,剩余的元素都會被初始化為 0.0。
成員函數
array 容器還提供有很多功能實用的成員函數。
- 迭代器函數:
begin()
,end()
,cbegin()
,cend()
,rbegin()
,rend()
,crbegin()
,crend()
。 size()
==max_size()
返回容器中當前元素的數量,其值始終等於初始化 array 類的第二個模板參數 N。empty()
判斷容器是否為空,和通過size()==0
判斷條件功能相同,但效率更高。at()
返回容器中 n 位置處元素的引用,該函數自動檢查 n 是否在有效的范圍內,如果不是則拋出 out_of_range 異常。用at()訪問相比下標方式更安全,但是因為多了安全檢查性能更低。front()
,back()
返回容器中第一個元素和最后一個元素的直接引用,該函數不適用於空的 array 容器。data()
返回一個指向容器首個元素的指針。利用該指針,可實現復制容器中所有元素等類似功能(values.data() == & values[0] == values.begin()
)。fill(val)
將 val 這個值賦值給容器中的每個元素。array1.swap(array2)
交換 array1 和 array2 容器中的所有元素,但前提是它們具有相同的長度和類型。
另外,在<array>
頭文件中還重載了get()
全局函數,該重載函數的功能是訪問容器中指定的元素,並返回該元素的引用。需要注意的是,該模板函數中,參數的實參必須是一個在編譯時可以確定的常量表達式,所以它不能是一個循環變量。也就是說,它只能訪問模板參數指定的元素,編譯器在編譯時會對它進行檢查。
std::cout << std::get<3>(values) << std::endl;
array遍歷
double total = 0;
for(size_t i = 0 ; i < values.size() ; ++i)
{
total += values[i];
}
double total = 0;
for(auto& value : values)
total += value;
for (auto i = values.begin(); i < values.end(); i++) {
cout << *i << " ";
}
#include <numeric>
int result = accumulate(values.begin(), values.end(), 0);
Vector
vector 容器是 STL 中最常用的容器之一,它和 array
容器非常類似,都可以看做是對 C++ 普通數組的“升級版”。不同之處在於,array
實現的是靜態數組(容量固定的數組),而 vector
實現的是一個動態數組,即可以進行元素的插入和刪除,在此過程中,vector
會動態調整所占用的內存空間,整個過程無需人工干預。
vector
常被稱為向量容器,因為該容器擅長在尾部插入或刪除元素,在常量時間內就可以完成,時間復雜度為 \(O(1)\);而對於在容器頭部或者中部插入或刪除元素,則花費時間要長一些(移動元素需要耗費時間),時間復雜度為線性階 \(O(n)\)。
初始化
vector()
:創建一個空vector
vector(int nSize)
:創建一個vector
,元素個數為nSizevector(int nSize,const t& t)
:創建一個vector
,元素個數為nSize,且值均為tvector(const vector&)
:復制構造函數vector(begin,end)
:復制[begin,end)
區間內另一個數組的元素到vector中
std::vector<double> values;
注意,這是一個空的 vector
容器,因為容器中沒有元素,所以沒有為其分配空間。當添加第一個元素(比如使用 push_back()
函數)時,vector
會自動分配內存。
在創建好空容器的基礎上,還可以像下面這樣通過調用 reserve()
成員函數來增加容器的容量:
values.reserve(20);
這樣就設置了容器的內存分配,即至少可以容納 20 個元素。注意,如果 vector
的容量在執行此語句之前,已經大於或等於 20 個元素,那么這條語句什么也不做;另外,調用 reserve()
不會影響已存儲的元素,也不會生成任何元素,即 values
容器內此時仍然沒有任何元素。
還需注意的是,如果調用
reserve()
來增加容器容量,之前創建好的任何迭代器(例如開始迭代器和結束迭代器)都可能會失效,這是因為,為了增加容器的容量,vector<T>
容器的元素可能已經被復制或移到了新的內存地址。所以后續再使用這些迭代器時,最好重新生成一下。
除了創建空 vector
容器外,還可以在創建的同時指定初始值以及元素個數,比如:
std::vector<int> primes {2, 3, 5, 7, 11, 13, 17, 19};
這樣就創建了有8個元素的 vector
容器。
在創建 vector
容器時,也可以指定元素個數:
std::vector<double> values(20);
注意,圓括號 () 和大括號 {} 是有區別的,前者(例如 (20) )表示元素的個數,而后者(例如 {20} ) 則表示 vector 容器中只有一個元素 20。
第二個參數指定了所有元素的初始值,因此這 20 個元素的值都是 1.0。
std::vector<double> values(20, 1.0);
還可以通過一個vector
初始化另一個vector
,或者一個vector
的迭代器范圍初始化另一個vector
std::vector<int> primes {2, 3, 5, 7, 11, 13, 17, 19};
std::vector<int> other_primes(primes);
std::vector<int> third_primes(primes.begin()+3, primes.end());
一些特殊初始化
初始化二維數組時,可以先確定第一維大小時,初始化如下:
下面兩種方法效果完全相同。
vector<vector<int>> nums(10);
vector<vector<int>> nums(10, vector<int>());
需要初始化二維零數組時,可以這樣初始化:
// 初始化二維 10x10 的0數組
vector<vector<int>> nums(10, vector<int>(10, 0));
需要將 vector
從 0 開始初始化:
vector<int> nums(10);
iota(nums.begin(), nums.end(), 0);
// nums = [0, 1, 2, ..., 8, 9]
成員函數
- 迭代器函數:同
array
增加數據
void push_back(const T& x)
:向量尾部增加一個元素Xiterator insert(iterator it,const T& x)
:向量中迭代器指向元素前增加一個元素xiterator insert(iterator it,int n,const T& x)
:向量中迭代器指向元素前增加n個相同的元素xiterator insert(iterator it,const_iterator first,const_iterator last)
:向量中迭代器指向元素前插入另一個相同類型向量的[first,last)
間的數據
刪除數據
iterator erase(iterator it)
:刪除向量中迭代器指向元素iterator erase(iterator first,iterator last)
:刪除向量中[first,last)
中元素void pop_back()
:刪除向量中最后一個元素void clear()
:清空向量中所有元素
迭代器
reference at(int pos)
:返回pos位置元素的引用reference front()
:返回首元素的引用reference back()
:返回尾元素的引用iterator begin()
:返回向量頭指針,指向第一個元素iterator end()
:返回向量尾指針,指向向量最后一個元素的下一個位置reverse_iterator rbegin()
:反向迭代器,指向最后一個元素reverse_iterator rend()
:反向迭代器,指向第一個元素之前的位置
其他函數
size()
返回實際元素個數max_size()
返回元素個數的最大值。這通常是一個很大的值,一般是 \(2^{32}-1\),所以我們很少會用到這個函數。resize()
改變實際元素個數capacity()
返回當前容量reserve()
改變容器容量shrink_to_fit()
將內存減少到等於當前元素實際所使用的大小。at()
使用經過邊界檢查的索引訪問元素,同array。operator[ ]
重載了[ ]
運算符,可以向訪問數組中元素那樣,通過下標即可訪問甚至修改 vector 容器中的元素front()
,back()
分別是第一個和最后一個元素的引用data()
返回指向容器中第一個元素的指針。emplace()
在指定的位置直接生成一個元素。emplace_back()
在序列尾部生成一個元素。
vector內存分配
和 array 容器不同,vector 容器可以隨着存儲元素的增加,自行申請更多的存儲空間。因此,在創建 vector 對象時,我們可以直接創建一個空的 vector 容器,並不會影響后續使用該容器。
但這會產生一個問題,即在初始化空的 vector 容器時,不能使用迭代器。也就是說,如下初始化 vector 容器的方法是不行的:
#include <iostream>
#include <vector>
int main()
{
std::vector<int> values;
int val = 1;
for (auto first = values.begin(); first < values.end(); ++first, val++) {
*first = val;
}
return 0;
}
除此之外,vector 容器在申請更多內存的同時,容器中的所有元素可能會被復制或移動到新的內存地址,這會導致之前創建的迭代器失效。
#include <iostream>
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v(1);
auto begin = &v[0]; // v.data() == &v[0]
int times = 0;
for (int i = 0; i < 10000000; ++i)
{
v.push_back(i);
if (begin != &v[0])
{
times++;
std::cout << "vector reallocator "<<times << " times" << " ......" << std::endl;
begin = &v[0];
}
}
return 0;
}
數據訪問
std::vector<int> values{1,2,3,4,5};
// 輸出第三個數
std::cout << *(values.data() + 2) << std::endl;
std::cout << values.at(2) << std::endl;
std::cout << values[2] << std::endl;
數據遍歷
std::vector<int> values{1,2,3,4,5};
// 使用迭代器遍歷
for(auto it = values.begin(); it!= values.end(); it++)
{
std::cout << *it << std::endl;
}
// 使用下標
for(size_t i = 0; i < values.size(); i++)
{
std::cout << values[i] << std::endl;
}
// c++11 新方法
for(auto &i: values)
{
std::cout << i << std::endl;
}