😎【博客主頁:你最愛的小傻瓜】😎
🤔【本文內容:C++ vector?😍? 】🤔
--------------------------------------------------------------------------------------------------------------------------------
在 C++ 的典籍長廊中,vector 恰似一位博聞強識的書吏。
它以動態舒展的卷軸承載萬千元素,不必憂慮篇幅局促 —— 新添內容時,只需輕揮 push_back 的筆墨,便有宣紙自然延展開來。那些沉睡的元素,如經卷中的字句般靜候翻閱,用下標輕輕一點,便能喚醒某頁某行的深意。
當你執迭代器之筆漫溯其間,如同指尖撫過古籍的竹箋,每一個元素都按序鋪陳,靜待細品。若想為這卷帙重整篇章,sort 函數便是最好的校勘師,轉瞬便能將凌亂的字句編排得井然有序。
無論是數字的珠璣、字符的墨韻,還是自定義對象的錦繡文章,它都能妥帖收納,恰似一座隨需而建的藏書樓,讓數據在其間各安其位,靜待編程者翻閱取用。
---------------------------------------------------------------------------------------------------------------------------------在開始學習vector容器前,我們先要了解vector的概念:
( 一 ) vector 的介紹:
1. vector 是可變大小數組的序列容器。它具備數組采用的連續存儲空間來存儲元素的特性,又優化了數組無法動態改變大小的缺點。而對于vector容器的動態分配數組,是一個有利也有弊的處理方式。當我們插入新的數據時,且vector容器需要增加存儲空間。這時他會重新開一個適合的數組來將原先的數組的數據賦值過去。而這里就有一個耗時的缺點。為了應對它,不同的庫會有不同的分配空間策略:(核心)
(二)vector的使用
1.構造:
接口說明 | |
---|---|
vector ()(重點) | 無參構造 |
vector (size_type n, const value_type& val = value_type()) | 構造并初始化 n 個 val |
vector (const vector& x);(重點) | 拷貝構造 |
vector (InputIterator first, InputIterator last); | 使用迭代器進行初始化構造 |
無參構造:就是構造一個空vector
//vector()
vector<int>v;
構造并初始化 n 個 val:
//vector (size_type n, const value_type& val = value_type())
vector<int>v(10,1);
拷貝構造:用已存在的?vector
?來構造新的?vector
// vector (const vector& x);
vector<int> v1{1, 2, 3};
vector<int> v2(v1);
使用迭代器進行初始化構造:利用其他容器(或?vector
?自身部分范圍)的迭代器來構造新?vector
// vector (InputIterator first, InputIterator last);
// 示例1:用數組迭代器構造
int arr[] = {1, 2, 3, 4, 5};
vector<int> v(arr, arr + 5);
// 示例2:用vector的部分迭代器構造
vector<int> v1{1, 2, 3, 4, 5};
vector<int> v2(v1.begin() + 1, v1.end() - 1);
2.迭代器訪問:
iterator 的使用 | 接口說明 |
---|---|
begin + end(重點) | 獲取第一個數據位置的 iterator/const_iterator,獲取最后一個數據的下一個位置的 iterator/const_iterator |
rbegin + rend | 獲取最后一個數據位置的 reverse_iterator,獲取第一個數據前一個位置的 reverse_iterator |
begin
?用于獲取容器中第一個數據位置的?iterator
(可修改元素)或?const_iterator
(不可修改元素,只讀);end
?用于獲取容器中最后一個數據的下一個位置的?iterator
?或?const_iterator
。
// begin + end(iterator 示例,可修改元素)
vector<int> v{1, 2, 3, 4, 5};
for (vector<int>::iterator it = v.begin(); it != v.end(); ++it) {*it *= 2; // 修改元素值
}
// begin + end(const_iterator 示例,只讀)
vector<int> v1{1, 2, 3, 4, 5};
for (vector<int>::const_iterator cit = v1.cbegin(); cit != v1.cend(); ++cit) {cout << *cit << " "; // 只能讀取元素值,不可修改
}
rbegin
?用于獲取容器中最后一個數據位置的?reverse_iterator
(反向迭代器,從后往前遍歷的起始位置);rend
?用于獲取容器中第一個數據前一個位置的?reverse_iterator
(反向遍歷的結束位置)。
// rbegin + rend(reverse_iterator 示例)
vector<int> v{1, 2, 3, 4, 5};
for (vector<int>::reverse_iterator rit = v.rbegin(); rit != v.rend(); ++rit) {cout << *rit << " "; // 從后往前遍歷并輸出元素
}
3.容量的訪問:
容量空間 | 接口說明 |
---|---|
size | 獲取數據個數 |
capacity | 獲取容量大小 |
empty | 判斷是否為空 |
resize(重點) | 改變 vector 的 size |
reserve(重點) | 改變 vector 的 capacity |
size
?用于獲取?vector
?中數據的個數。
vector<int> v{1, 2, 3, 4, 5};
cout << "數據個數:" << v.size() << endl;
capacity
?用于獲取?vector
?的容量大小(即當前為?vector
?分配的存儲空間能容納的元素個數)。
vector<int> v;
v.push_back(1);
cout << "容量大小:" << v.capacity() << endl;
empty
?用于判斷?vector
?是否為空(即是否沒有元素)
vector<int> v;
if (v.empty()) {cout << "vector 為空" << endl;
} else {cout << "vector 不為空" << endl;
}
resize
?用于改變?vector
?的?size
(元素個數)。若新?size
?大于原?size
,會用指定值(若未指定則用默認構造值)填充新增位置;若新?size
?小于原?size
,則會刪除多余元素。
// resize 示例1:新 size 大于原 size,用默認值填充
vector<int> v{1, 2, 3};
v.resize(5);
for (int num : v) {cout << num << " "; // 輸出:1 2 3 0 0
}
cout << endl;
// resize 示例2:新 size 大于原 size,用指定值填充
vector<int> v1{1, 2, 3};
v1.resize(5, 10);
for (int num : v1) {cout << num << " "; // 輸出:1 2 3 10 10
}
cout << endl;
// resize 示例3:新 size 小于原 size
vector<int> v2{1, 2, 3, 4, 5};
v2.resize(3);
for (int num : v2) {cout << num << " "; // 輸出:1 2 3
}
reserve
?用于改變?vector
?的?capacity
(容量),提前為?vector
?分配足夠的存儲空間,避免后續多次擴容帶來的性能開銷。
vector<int> v;
v.reserve(10); // 提前分配能容納 10 個元素的空間
cout << "容量大小:" << v.capacity() << endl;
4.增刪查改:
vector 增刪查改 | 接口說明 |
---|---|
push_back(重點) | 尾插 |
pop_back(重點) | 尾刪 |
find | 查找。(注意這個是算法模塊實現,不是 vector 的成員接口) |
insert | 在 position 之前插入 val |
erase | 刪除 position 位置的數據 |
swap | 交換兩個 vector 的數據空間 |
operator [](重點) | 像數組一樣訪問 |
push_back
?用于在?vector
?末尾插入元素(尾插)。
vector<int> v;
v.push_back(1);
v.push_back(2);
// 此時v中的元素為[1, 2]
pop_back
?用于刪除?vector
?末尾的元素(尾刪)
vector<int> v{1, 2, 3};
v.pop_back();
// 此時v中的元素為[1, 2]
insert
?用于在指定位置(position
?迭代器指向的位置之前)插入元素
vector<int> v{1, 3, 4};
// 在第二個元素位置(值為3的位置)前插入2
v.insert(v.begin() + 1, 2);
// 此時v中的元素為[1, 2, 3, 4]
erase
?用于刪除指定位置(position
?迭代器指向的位置)的元素。
vector<int> v{1, 2, 3, 4};
// 刪除第三個元素(值為3的元素)
v.erase(v.begin() + 2);
// 此時v中的元素為[1, 2, 4]
operator[]
?用于像訪問數組元素一樣,通過下標訪問?vector
?中的元素。
vector<int> v{10, 20, 30};
cout << v[0] << " " << v[1] << " " << v[2] << endl; // 輸出:10 20 30
v[1] = 25;
cout << v[1] << endl; // 輸出:25
(三) vector 模擬實現(代碼里面的注釋很重要):
開始了解vector:對于vector類,由于要儲存不同種類的數據,那么就需要模板來實現了。
namespace xin
{template<class T >class vector{public:typedef T* iterator; //常規的迭代器,可以讀寫typedef const T* const_iterator;//const修飾的迭代器,只能讀無法寫。//模擬實現 private:iterator _start = nullptr; //指向vector開頭的數據iterator _finish = nullptr; //指向vector最后一個的數據iterator endofstorage = nullptr;//指向vector存儲空間最后的位置};
};
我們先是像實現string類構建一個自己的命名空間域。接著寫出模板,然后寫迭代器(這里是指針),再在類里面的private里面構建成員變量。(這些就是我們先要了解的)
下一步完善vector:
1.空vector的構造:
vector()
{}
2.析構函數:
~vector()
{if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;}
}
說到底:析構函數就是將之前申請的空間還給操作系統,然后將之前所用到的指針賦值為空,防止野指針的出現。
3.迭代器:
//迭代器的訪問方式:
iterator begin()//可讀寫,且在函數內部能進行修改所屬對象。
{return _start;//返回頭指針
}
iterator end()
{return _finish;//返回指向vector最后一個的數據的指針
}
const_iterator begin()const//1.只能讀取容器中的元素,不能修改元素的值。2.在該函數內部,不能修改所屬對象
{return _start;
}
const_iterator end()const
{return _finish;
}
通過迭代器來去訪問vector數據的開頭的指針,和指向vector最后一個的數據的指針。
迭代器這個訪問方式是每個容器都有的相同訪問方式。優勢:
1.統一接口
2.抽象底層實現
3.便于算法復用
4.支持范圍操作
4.size:
size_t size()const //加這個const是為了在該函數內部,不能修改所屬對象。
{return _finish - _start;//這里用指針減指針的方式來計算數據的個數
}
計算vector里面存儲的數據大小。
5.capacity:
size_t capacity()const//加這個const是為了在該函數內部,不能修改所屬對象
{return _endofstorage - _start;//這里用指針減指針的方式來計算存儲空間的大小
}
計算vector 存儲空間大小。
6.reserve:
void reserve(size_t n)
{if (n > capacity())//擴容的條件。{size_t sz = size();//計算原數組的數據大小T* tmp = new T[n];//申請新的數組//memcpy(tmp, _start, sizeof(T) * sz);//這里為什么不用 memcpy來將原先的數據賦值給新建的數組,是因為深拷貝的問題 兼容自定義類型的深拷貝需求 for (int i = 0;i < sz;i++)//for循環的更安全可靠。{tmp[i] = _start[i];}delete[] _start;//將這些指針給調到適合的位置。_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}
}
保留:為vector預留空間。
對于不用memcpy是因為:
當拷貝?
string
?這類包含指針成員的自定義類型時,若使用?memcpy
?進行拷貝,會引發淺拷貝問題。自定義類型(如?
std::string
)一般由棧上的指針(用于管理內部字符數組等)和指針指向的堆內存(存儲實際數據)組成。memcpy
?只是二進制層面復制棧上的指針值,不會復制指針指向的堆內存,這會導致:
- 兩個對象的指針成員指向同一塊堆內存(淺拷貝);
- 析構時對同一塊內存多次釋放(
double free
),造成程序崩潰;- 一個對象修改數據會影響另一個對象,破壞拷貝獨立性。
對于?
vector
,本身是深拷貝,但當?vector
?中存儲的是?string
?數組或其他含指針成員的自定義類型數組時,若用?memcpy
?處理,也會因淺拷貝而出錯。
7.resize(調整容器大小):
void resize(size_t n, const T& val = T())//T()是指確保生成一個符合 “默認初始化” 語義的 T 類型對象。
{if (n < size()){_finish = _start + n;//(比size()小時,我們直接截取到我們想要的n個數據)}else{reserve(n);while (_finish != _start + n)//比size()大時,我們就重新申請空間,在將后面的空間賦值T(){*_finish = val;++finish;}}
}
在 C++ 中,
T()
?是一種值初始化語法,用于創建?T
?類型的默認構造對象,這是由 C++ 標準規定的語法規則:
對于內置類型(如 int、double 等)
T()
?會生成該類型的 “零值”。例如:
int()
?等價于?0
double()
?等價于?0.0
- 指針類型?
int*()
?等價于?nullptr
對于自定義類型(類 / 結構體)
T()
?會顯式調用該類型的默認構造函數(即無參數的構造函數)。如果類中沒有定義任何構造函數,編譯器會自動生成一個默認構造函數,此時?T()
?會使用這個編譯器生成的版本。
8.insert(pos的位置插入):
iterator insert(iterator pos, const T& x)
{assert(pos >= _start && pos <= _finish);//判斷pos是否在數組內if (_finish == _endofstorage)//判斷兩種請況:空 ,兩個不同概念的尾部指針重合.以及處理方式。{size_t len = pos - _start;//為處理迭代器失效而留下的坐標。size_t new_capacity = capacity() == 0 ? 4 : capacity() * 2;reserve(new_capacity);pos = _start + len;//解決迭代器失效的問題}iterator end = _finish - 1;//這里減一是為了更好的訪問和挪移數據。while (end > pos)//往后挪移{*(end + 1) = *end;--end;}//插入數據*pos = x;++_finish;return pos;
}
pos位置的插入。這里有個迭代器失效的問題我們要注意一下,并去解決。
迭代器失效的本質
迭代器底層多是指針或指針封裝(如?
vector
?迭代器類似原生指針?T*
)。迭代器失效,是指其底層對應指針指向的空間被銷毀,繼續使用會導致程序崩潰。解決擴容導致內部迭代器失效的關鍵邏輯通過?size_t len = pos - _start;
?和?pos = _start + len;
?兩行代碼解決:
- 記錄偏移量:
len = pos - _start
?計算插入位置?pos
?相對于容器起始地址?_start
?的偏移量(相對位置,不受內存釋放 / 移動影響)。- 重定位迭代器:擴容時舊內存釋放、
_start
?指向新內存,利用?pos = _start + len
?重新確定?pos
?在新內存中的位置。避免失效的原因
- 絕對地址(如舊內存地址)會因內存釋放失效,但偏移量是相對位置,不受內存塊移動影響。
- 即便?
pos
?原本指向特殊地址,偏移量計算仍能準確定位新內存中的位置。關于?
pos
?為 0 的小知識點有效內存空間的地址不會是 0,0 是操作系統預留的 “無效地址”(對應?
nullptr
),不會分配給用戶程序正常內存空間。所以指向容器有效范圍的迭代器,底層指針絕不可能為 0,不存在 “pos
?為 0” 的隱患。返回值?
pos
?的作用用于解決外部迭代器失效,防止類似 “先獲取迭代器,再執行插入操作后,原迭代器因容器內部變化而失效” 的問題
外部迭代器失效問題
外部代碼若保存插入前的迭代器,當插入觸發?
vector
?擴容時,舊內存被釋放,該迭代器會指向無效地址(失效)。若繼續使用失效迭代器(如?it += 10
),會訪問已釋放內存,引發未定義行為(如程序崩潰),這類操作屬于高危行為。解決方法
?
insert
?操作會返回新的有效迭代器,應改用該返回值來操作,而非使用插入前可能失效的舊迭代器。示例代碼:auto it = vec.begin() + 1; // 插入后,it可能失效,改用返回的新迭代器 auto new_it = vec.insert(it, 200); *new_it += 10; // 安全,new_it指向新內存中正確位置
insert
?返回值的意義提供擴容后已重定位的有效迭代器,替代可能失效的舊迭代器。
迭代器失效總結
- 內部防失效:通過偏移量?
len
?記錄相對位置,確保護容后?pos
?在新內存中正確定位,保證?insert
?內部邏輯正常。- 外部防失效:
insert
?返回更新后的有效迭代器,提醒用戶放棄使用可能失效的舊迭代器,改用新迭代器。
9.push_back(尾插):
void push_back(const T& x)//尾插
{/*if (_finish == _endofstorage)//判斷數據情況,如容器是否為空,為空的處理方式,不為空的處理方式。{size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}//尾插。*_finish = x;++_finish;*/insert(end(),x);
}
10.構造函數和拷貝構造函數:
vector(size_t n, const T& val = T())//這里的構造就像當于截取,但因為是初始化,剛開始_start = _finish,所以他是從開頭賦值初始化
{resize(n, val);
}
vector(int n, const T& val = T())//這個是因為當我們在傳參的時有我們傳<int int>時,編譯器會更具“精確匹配優先于需要隱式轉換的匹配”,會到迭代器哪里但int又不是迭代器,所以會報錯。
{resize(n, val);
}
template<class InputIterator>
vector(InputIterator first, InputIterator last)//對于迭代器的初始化,而這里為什么用push_back呢,這是因為當我們不知道你要存儲的數據大小時,我們只能一個一個填下去,盡管他是會遇到vector的這一缺點但還是要這樣做,因為不知道有大小。
{while (first != last){push_back(*first);//因為剛開始_start = _finish。++first;}
}vector(const vector<T>& v)
{_start = new T[v.capacity()];//拷貝構造,申請空間//memcpy(_start,v._start,sizeof(T)*v.size);//是因為不適合自定義類形,淺拷貝與深拷貝的問題for (size_t i = 0;i < v.size();i++){_start[i] = v._start[i];}//為其賦值。_finish = _start + v.size();_endofstorage = _start + v.capacity();
}
//舊版
/*vector(const vector<T>& v):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(v.capacity());for (auto e : v){push_back(e);}}*/
這里有一個概念要拿出來講:
那便是精確匹配優先于需要隱式轉換的匹配
對于:
vector<int>v(10,1);
你認為會用哪構造(如果沒有int這個顯示的),是size_t, 還模板的那個。他會是模板那個。因為編譯器會把10,1都看成int,滿足InputIterator first, InputIterator last的參數類型要求(兩個參數類型相同)因此它符合精確匹配優先于需要隱式轉換的匹配。而int —》size_t這個隱式轉換優先級低。
解決方法:
核心就是不讓它隱式轉換。
1.外:
vector <int> v(10u, 1);
2.內:
vector(int n, const T& value = T()) {resize(n, value); }
11.erase(刪除pos位置數據):
iterator erase(iterator pos)
{assert(pos >= _start && pos <= _finish);//判斷pos是否在數組內iterator it = pos + 1;//這里加一是為了更好的訪問和挪移數據while (it != _finish)//往前覆蓋{*(it - 1) = *it;++it;}--_finish;return pos;
}
迭代器失效的定義與標準規定
對?
vector
?執行?erase(pos)
?后,被刪除元素的迭代器?pos
?及其之后的所有迭代器都會失效。此時對失效迭代器進行解引用(如?*it
)或遞增(如?++it
)操作,屬于未定義行為(標準不規定執行結果,編譯器可自由處理,可能崩潰、輸出錯誤結果甚至 “看似正常運行”)。不同平臺的表現差異
- VS:對迭代器有效性做嚴格額外檢查,若檢測到使用失效迭代器,會直接報錯或崩潰,能提前暴露問題。
- Linux:默認不做強制檢查,失效迭代器可能恰好指向未回收內存,導致程序 “看似能運行”,但這是巧合,本質仍為錯誤代碼。
錯誤代碼示例與問題
// 錯誤示例:刪除所有偶數for (auto it = v.begin(); it != v.end(); ++it) {if (*it % 2 == 0) {v.erase(it); // 刪除后,it已失效// 此處it指向被刪除元素的下一個位置,但標準未定義其有效性}}// 輸出結果可能異常(如跳過元素、循環提前結束等)for (int num : v) {cout << num << " ";}return 0;
如刪除偶數的代碼(
erase
?后直接?++it
),會因迭代器失效出現邏輯錯誤(如跳過元素),且刪除本質是移動數據覆蓋待刪數據,會導致?finish
?前移,后續?it
?可能永遠不等于?end
,循環無法正確結束。看似 “可行” 代碼的隱患
即使在 Linux 下 “通過測試” 的代碼,也存在問題:
- 不符合 C++ 標準,
erase(it)
?后?it
?已失效,后續?it != v.end()
?判斷也可能出錯。- 移植性極差,換用 VS 等編譯器或調試模式會暴露崩潰 / 邏輯錯誤。
- 若觸發擴容(內存重分配),失效?
it
?指向舊內存塊,操作會導致嚴重錯誤。正確做法
vector
?刪除元素時,必須通過?erase
?的返回值更新迭代器,這是唯一能保證跨平臺一致性和程序正確性的做法。其他 “看似可行” 的寫法,本質是依賴未定義行為的僥幸,隱藏著難以排查的風險。判斷代碼正確性,應看是否符合語言標準,而非 “在某平臺能運行”。
12.尾刪:
void pop_back()
{erase(--end());
}
直接用erase任意位置刪除,如果不想用erase也可以在erase里面的代碼里找刪除的原理再套用在尾部。
13.重載:
void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);
}// v1 = v2
vector<T>& operator=(vector<T> v)
{swap(v);return *this;
}
T& operator[](size_t pos)
{assert(pos < size());return _start[pos];
}const T& operator[](size_t pos) const
{assert(pos < size());return _start[pos];
}
這里的重載都與strin類里面的類似。主要是swap這個交換,以及對_start指針的運用。
完整代碼:
#include<assert.h>
#include<string>
#include<iostream>
using namespace std;
namespace xin
{template<class T >class vector{public:typedef T* iterator; //常規的迭代器,可以讀寫 typedef const T* const_iterator;//const修飾的迭代器,只能讀無法寫。//析構函數:~vector(){if (_start){delete[] _start; //從頭指針開始找 釋放空間_start = _finish = _endofstorage = nullptr; //將指針賦值為空}}//迭代器的訪問方式:iterator begin()//可讀寫,且在函數內部能進行修改所屬對象。{return _start;//返回頭指針}iterator end()//可讀寫,且在函數內部能進行修改所屬對象。{return _finish;//返回指向vector最后一個的數據的指針}const_iterator begin()const//1.只能讀取容器中的元素,不能修改元素的值。2.在該函數內部,不能修改所屬對象{return _start;}const_iterator end()const//1.只能讀取容器中的元素,不能修改元素的值。2.在該函數內部,不能修改所屬對象{return _finish;}size_t size()const //加這個const是為了在該函數內部,不能修改所屬對象。{return _finish - _start;//這里用指針減指針的方式來計算數據的個數}size_t capacity()const//加這個const是為了在該函數內部,不能修改所屬對象{return _endofstorage - _start;//這里用指針減指針的方式來計算存儲空間的大小}void reserve(size_t n){if (n > capacity())//擴容的條件。{size_t sz = size();//計算原數組的數據大小T* tmp = new T[n];//申請新的數組//memcpy(tmp, _start, sizeof(T) * sz);//這里為什么不用 memcpy來將原先的數據賦值給新建的數組,是因為深拷貝的問題 兼容自定義類型的深拷貝需求 for (int i = 0;i < sz;i++)//for循環的更安全可靠。{tmp[i] = _start[i];}delete[] _start;//將這些指針給調到適合的位置。_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}}void resize(size_t n, const T& val = T())//T()是指確保生成一個符合 “默認初始化” 語義的 T 類型對象。{if (n < size()){_finish = _start + n;//(比size()小時,我們直接截取到我們想要的n個數據)}else{reserve(n);while (_finish != _start + n)//比size()大時,我們就重新申請空間,在將后面的空間賦值T(){*_finish = val;++finish;}}}iterator insert(iterator pos, const T& x){assert(pos >= _start && pos <= _finish);//判斷pos是否在數組內if (_finish == _endofstorage)//判斷兩種請況:空 ,兩個不同概念的尾部指針重合.以及處理方式。{size_t len = pos - _start;//為處理迭代器失效而留下的坐標。size_t new_capacity = capacity() == 0 ? 4 : capacity() * 2;reserve(new_capacity);pos = _start + len;//解決迭代器失效的問題}iterator end = _finish - 1;//這里減一是為了更好的訪問和挪移數據。while (end > pos)//往后挪移{*(end + 1) = *end;--end;}//插入數據*pos = x;++_finish;return pos;}void push_back(const T& x)//尾插{if (_finish == _endofstorage)//判斷數據情況,如容器是否為空,為空的處理方式,不為空的處理方式。{size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}//尾插。*_finish = x;++_finish;insert(_finish, x);}vector(size_t n, const T& val = T())//這里的構造就像當于截取,但因為是初始化,剛開始_start = _finish,所以他是從開頭賦值初始化{resize(n, val);}vector(int n, const T& val = T())//這個是因為當我們在傳參的時有我們傳<int int>時,編譯器會更具“精確匹配優先于需要隱式轉換的匹配”,會到迭代器哪里但int又不是迭代器,所以會報錯。{resize(n, val);}template<class InputIterator>vector(InputIterator first, InputIterator last)//對于迭代器的初始化,而這里為什么用push_back呢,這是因為當我們不知道你要存儲的數據大小時,我們只能一個一個填下去,盡管他是會遇到vector的這一缺點但還是要這樣做,因為不知道有大小。{while (first != last){push_back(*first);//因為剛開始_start = _finish。++first;}}vector(const vector<T>& v){_start = newT[v.capacity];//拷貝構造,申請空間//memcpy(_start,v._start,sizeof(T)*v.size);//是因為不適合自定義類形for (size_t i = 0;i < v.size;i++){_start[i] = v._start[i];}//為其賦值。_finish = _start + v.size;_endofstorage = _start + v.capacity;}iterator erase(iterator pos){assert(pos >= _start && pos <= _finish);//判斷pos是否在數組內iterator it = pos + 1;//這里加一是為了更好的訪問和挪移數據while (it != _finish)//往前覆蓋{*(it - 1) = *it;++it;}--_finish;return pos;}void swap(const vector<T>& v){_start = v._start;_finish = v._finish;_endofstorage = v._endofstorage;}vector<T>& operator=(vector<T>& v){swap(v);return *this;}T& operator [](size_t pos){assert(pos < size());return _start[pos];}const T& operator [](size_t pos)const{assert(pos < size());return _start[pos];}private:iterator _start = nullptr; //指向vector開頭的數據iterator _finish = nullptr; //指向vector最后一個的數據iterator _endofstorage = nullptr;//指向vector存儲空間最后的位置};
};