今日分享:C++ -- vector

😎【博客主頁:你最愛的小傻瓜】😎

🤔【本文內容:C++ vector?😍? 】🤔

--------------------------------------------------------------------------------------------------------------------------------

在 C++ 的典籍長廊中,vector 恰似一位博聞強識的書吏。

它以動態舒展的卷軸承載萬千元素,不必憂慮篇幅局促 —— 新添內容時,只需輕揮 push_back 的筆墨,便有宣紙自然延展開來。那些沉睡的元素,如經卷中的字句般靜候翻閱,用下標輕輕一點,便能喚醒某頁某行的深意。

當你執迭代器之筆漫溯其間,如同指尖撫過古籍的竹箋,每一個元素都按序鋪陳,靜待細品。若想為這卷帙重整篇章,sort 函數便是最好的校勘師,轉瞬便能將凌亂的字句編排得井然有序。

無論是數字的珠璣、字符的墨韻,還是自定義對象的錦繡文章,它都能妥帖收納,恰似一座隨需而建的藏書樓,讓數據在其間各安其位,靜待編程者翻閱取用。

---------------------------------------------------------------------------------------------------------------------------------在開始學習vector容器前,我們先要了解vector的概念:

( 一 ) vector 的介紹:

1. vector 是可變大小數組的序列容器。它具備數組采用的連續存儲空間來存儲元素的特性,又優化了數組無法動態改變大小的缺點。而對于vector容器的動態分配數組,是一個有利也有弊的處理方式當我們插入新的數據時,且vector容器需要增加存儲空間。這時他會重新開一個適合的數組來將原先的數組的數據賦值過去而這里就有一個耗時的缺點為了應對它,不同的庫會有不同的分配空間策略:(核心)

分配一些額外的空間以適應可能的增長,因為存儲空間比實際需要的存儲空間更大。但具體是由實現者來進行怎樣分配空間的。
無論如何,重新分配都應該是對數增長的間隔大小(比如每次擴容為原來的 1.5 倍或 2 倍),以至于在末尾插入一個元素的時候是在常數時間的復雜度完成的,不會觸發重新分配空間。
vector容器與其它動態序列容器相比(deque, list and forward_list),其最大優勢:訪問元素的時候更加高效,在末尾添加和刪除元素相對高效。相對的,vector不在末尾的刪除和插入操作效率更低。?
還有:由于存儲空間連續?????????vector 的迭代器和引用,支持隨機訪問(像數組一樣直接跳著訪問元素)、更穩定(只要不擴容,引用和迭代器基本不會失效),而且在遍歷、隨機取元素時速度更快;而list and forward_list?的迭代器只能逐個挪,還容易失效,效率也低。

(二)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++ 標準規定的語法規則:

  1. 對于內置類型(如 int、double 等)
    T()?會生成該類型的 “零值”。例如:

    • int()?等價于?0
    • double()?等價于?0.0
    • 指針類型?int*()?等價于?nullptr
  2. 對于自定義類型(類 / 結構體)
    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存儲空間最后的位置};
};

??總結

相信堅持下來的你一定有了滿滿的收獲。那么也請老鐵們多多支持一下,為愛博,點點舉報,偶布,是點點關注,收藏,點贊。??

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/95368.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/95368.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/95368.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

NAS Docker 安裝N8N

NAS Docker 安裝N8Ndocker 操作中文版使用 Docker Compose&#xff08;更易于管理&#xff09;創建一個 docker-compose.yml 文件&#xff0c;內容如下&#xff1a;yaml version: 3services:n8n:image: n8nio/n8n:latestcontainer_name: n8nrestart: unless-stoppedports:- &q…

Node.js漢字轉拼音指南:pinyin-pro全解析

pinyin-pro 工具庫簡介核心功能&#xff1a;漢字轉拼音、多音字處理、音調控制、格式定制等性能特點&#xff1a;高效、輕量級、支持多種拼音風格應用場景&#xff1a;搜索優化、數據排序、中文輸入法等環境準備與安裝Node.js npm 或 yarn 安裝 pinyin-pronpm install pinyin-p…

UART-TCP雙向橋接服務

UART-TCP雙向橋接服務是一種將串口&#xff08;UART&#xff09;通信與TCP/IP網絡通信相互轉換的技術服務&#xff0c;其核心功能是實現兩種不同協議之間的數據透明傳輸。1. 基本概念UART&#xff08;串口&#xff09;&#xff1a;硬件設備的傳統通信接口&#xff0c;常見于嵌入…

江協科技STM32學習筆記補充之001。為什么C語言在對STM32編程過程中的二進制要用十六進制來進行讀寫。而不能直接用二進制來進行讀寫。

下面給你一個“為什么嵌入式 C&#xff08;如 STM32&#xff09;普遍用十六進制而不是二進制來讀寫寄存器/地址”的系統性分析。核心觀點&#xff1a;十六進制是對底層位模式更高效、更可靠的“人類可讀編碼”&#xff0c;與硬件資料、編譯器和調試器生態形成了標準化協同。1&a…

從 “對話” 到 “共創”:生成式 AI 如何重塑內容創作全流程,普通人也能掌握的高效工具指南

一、引言&#xff1a;內容創作的 “AI 范式轉移”—— 從單向輸出到雙向共創?傳統內容創作痛點&#xff1a;靈感枯竭、流程繁瑣&#xff08;選題 - 調研 - 初稿 - 修改 - 定稿耗時久&#xff09;、專業門檻高&#xff08;如設計需掌握 PS、寫作需深厚文字功底&#xff09;?生…

函數、數組與 grep + 正則表達式的 Linux Shell 編程進階指南

文章目錄1.函數相關2.數組相關3.正則表達式與grep根據你提供的內容&#xff0c;我整理了一份關于Shell腳本中函數、數組和正則表達式的簡明參考&#xff1a; 1.函數相關 函數調用&#xff1a; 直接使用函數名調用&#xff1a;函數名 參數傳遞&#xff1a; 函數內接收參數&…

nginx-realip問題解決方案

nginx-realip問題解決方案一、配置真實ip解析二、日志中記錄真實 IP三、在日志中驗證一、配置真實ip解析 讓backend server知道前端是誰來訪問的&#xff0c;知道他們的ip地址 LB在轉發數據包的時候&#xff0c;在http請求報文里增加一個字段&#xff0c;攜帶user的ip地址&am…

Kafka入門指南:從安裝到集群部署

一、Kafka 基礎與系統要求 1.1 核心概念 Broker&#xff1a;Kafka 服務器節點&#xff0c;負責存儲消息和處理客戶端請求 Topic&#xff1a;消息分類的邏輯容器&#xff0c;每條消息需指定發送到某個 Topic Partition&#xff1a;Topic 的物理分片&#xff0c;可分布式存儲…

20250828在榮品RD-RK3588-MID開發板的Android13系統下適配Bainianxing的GPS模塊BU-16M10

20250828在榮品RD-RK3588-MID開發板的Android13系統下適配Bainianxing的GPS模塊BU-16M10 2025/8/29 9:50榮品RD-RK3588-MID開發板。適配GPS 38400bps 需要配置波特率嗎&#xff1f;一般是 9600這邊使用的泰斗 你要適配新的gps模塊&#xff1f;規格書&#xff1a;Baud rate 3840…

對部分國家(地區)出口商品類章金額數據庫

一、數據庫簡介【艾思產研數據平臺】對部分國家(地區)出口商品類章金額數據庫&#xff0c;收錄了2015年02月 - 2025年5月的信息&#xff0c;共計49萬余條數據&#xff0c;整理出7個常用字段內容。更新頻率為月更。字段內容年月、類章、國家、國家id、所屬分類、月出口商品類章金…

STM32——中斷

總&#xff1a;STM32——學習總綱 一、什么是中斷 1.1 作用與意義 1.2 STM32 GPIO 外部中斷簡圖 二、NVIC 2.1 NVIC 基本概念 Nested vectored interrupt controller&#xff0c;嵌套向量中斷控制器&#xff0c;屬于內核&#xff08;M3、M4、M7&#xff09; 用不到很多的優先…

DVWA靶場通關筆記-Weak Session IDs (Impossible級別)

目錄 一、Session ID 二、源碼分析 1、index.php 2、impossible.php 三、Weak Session IDs安全級別對比 四、impossible防范方法分析 1、高隨機性會話 ID 生成 2、嚴格的 Cookie 作用域限制 3、安全的傳輸與存儲控制期 本系列為通過《DVWA靶場通關筆記》的Weak Sessio…

SyncBack 備份同步軟件: 使用 FTPS、SFTP 和 HTTPS 安全加密傳輸文件

傳輸加密是使用安全連接在網絡中傳輸數據&#xff08;例如文件&#xff09;的過程。TLS&#xff08;傳輸層安全&#xff09;、SSL&#xff08;安全套接字層&#xff09;、SSH&#xff08;安全套接字外殼&#xff09;、HTTPS&#xff08;基于 SSL/TLS 的超文本傳輸協議&#xff…

保健品跨境電商:如何筑牢產品質量與安全防線?

保健品跨境電商&#xff1a;如何筑牢產品質量與安全防線&#xff1f;在保健品跨境電商領域&#xff0c;“質量與安全”是消費者信任的基石&#xff0c;也是品牌長期發展的生命線。從海外工廠生產到國內消費者手中&#xff0c;產品需經歷“跨國運輸、清關核驗、倉儲配送”多環節…

手把手教你搭建 UDP 多人聊天室(附完整源碼)

一、項目介紹 本文將分享一個基于 UDP 協議的簡易多人聊天室項目&#xff0c;包含服務器端和客戶端的完整實現。該聊天室支持多客戶端同時連接&#xff0c;能實現消息群發、用戶加入 / 退出通知等核心功能&#xff0c;適合作為網絡編程入門實踐案例。項目采用 C 語言開發…

Vue基礎知識-使用監視屬性watch和計算屬性computed實現列表過濾+排序

一、完整源碼<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><script src…

自動化運維-ansible中的管理機密

自動化運維-ansible中的管理機密 一、Ansible Vault 在自動化配置管理中&#xff0c;直接以純文本形式存儲密碼、API密鑰、證書等敏感信息是極大的安全漏洞。Ansible Vault 正是為了解決這一問題而設計的核心功能 Ansible Vault 是 Ansible 的一個核心功能&#xff0c;它允許用…

UFUNCTION C++ 的再次理解

一.UFUNCTION 格式和屬性也比較像&#xff0c;兩部分 函數說明符&#xff0c;和元數據說明符UFUNCTION不僅能 控制對藍圖公開&#xff0c;還能與 綁定委托&#xff0c;用戶輸入,網絡回調功能相關聯&#xff0c;而且還能創建自己控制帶命令二.函數說明符控制 &#xff0c;函數在…

《論文閱讀》從心到詞:通過綜合比喻語言和語義上下文信號產生同理心反應 2025 ACL findings

《論文閱讀》從心到詞:通過綜合比喻語言和語義上下文信號產生同理心反應 2025 ACL findings 前言 創新點 形象語言 (Figurative Language) 語義上下文信號(Semantic Context Signals) 模型架構 情緒原因標注 形象語言元數據獲取 共情回復生成 實驗結果 總結 趨勢 前言 親…

MySQL內置的各種單行函數

精選專欄鏈接 &#x1f517; MySQL技術筆記專欄Redis技術筆記專欄大模型搭建專欄Python學習筆記專欄深度學習算法專欄 歡迎訂閱&#xff0c;點贊&#xff0b;關注&#xff0c;每日精進1%&#xff0c;與百萬開發者共攀技術珠峰 更多內容持續更新中&#xff01;希望能給大家帶來…