最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++ vector從入門到模擬實現(xiàn)過程

 更新時間:2026年06月02日 10:22:13   作者:晚風吹紅霞  
這段文章詳細講解了C++ STL中的vector容器,從其優(yōu)勢、基礎刪查改操作到迭代器失效等,并提供了模擬實現(xiàn)和常見陷阱的解析,幫助開發(fā)者全面掌握vector的用法,重點強調(diào)了reserve的用法、迭代器失效的處理和深拷貝、淺拷貝的區(qū)別,感興趣的朋友一起看看吧

一、為什么 vector 是“最強容器”?

在 C++ 標準模板庫(STL)中,vector 是一個動態(tài)數(shù)組。它與普通數(shù)組的區(qū)別在于:

  • 大小可以自動增長(不需要手動 realloc)。
  • 支持隨機訪問([] 運算符,O(1) 時間)。
  • 在尾部增刪元素效率高(均攤 O(1))。
  • 提供豐富的成員函數(shù),如 push_back、pop_backinsert、erase 等。

學習 STL 有三個境界:能用 → 明理 → 能擴展。本文會幫你至少達到第二個境界,并向第三個境界邁進。

二、vector 的基礎使用

使用 vector 需要包含 <vector> 頭文件,并引入 std 命名空間。

2.1 構造方式

#include <vector>
using namespace std;
int main() {
    vector<int> v1;               // 空 vector
    vector<int> v2(5, 10);        // 5 個元素,每個都是 10
    vector<int> v3(v2);           // 拷貝構造
    vector<int> v4(v2.begin(), v2.end()); // 迭代器區(qū)間構造
    int arr[] = {1,2,3,4};
    vector<int> v5(arr, arr+4);   // 使用數(shù)組構造
    return 0;
}

2.2 迭代器與遍歷

vector 支持隨機訪問迭代器,常用方式有三種:

vector<int> v = {1, 2, 3, 4, 5};
// 1. 下標 operator[]
for (size_t i = 0; i < v.size(); ++i)
    cout << v[i] << " ";
// 2. 迭代器
for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
    cout << *it << " ";
// 3. C++11 范圍 for
for (auto e : v)
    cout << e << " ";

2.3 容量相關:size、capacity、resize、reserve

這是 vector 新手最容易混淆的地方。

  • size():當前實際存儲的元素個數(shù)。
  • capacity():當前分配的內(nèi)存能容納的元素個數(shù)(capacity >= size)。
  • resize(n, val):改變 size 為 n。若 n > size,則用 val 填充(默認用 0 或默認構造);若 n < size,則截斷。它會影響 size,也可能改變 capacity
  • reserve(n):預留至少 n 個元素的空間(改變 capacity 但不改變 size)。常用于提前知道元素數(shù)量,避免多次擴容。
vector<int> v;
v.reserve(100);          // 容量至少 100,size 仍為 0
for (int i = 0; i < 100; ++i)
    v.push_back(i);      // 不會觸發(fā)擴容
cout << v.size() << " " << v.capacity() << endl; // 100 至少100

2.4 擴容機制:1.5 倍還是 2 倍?

不同 STL 實現(xiàn)的擴容策略不同,這是一個??嫉狞c。

  • VS (微軟):按 1.5 倍 擴容。
  • g++ (Linux / SGI STL):按 2 倍 擴容。

測試代碼:

vector<int> v;
size_t sz = v.capacity();
for (int i = 0; i < 100; ++i) {
    v.push_back(i);
    if (sz != v.capacity()) {
        sz = v.capacity();
        cout << "capacity changed: " << sz << endl;
    }
}

VS 輸出示例:1, 2, 3, 4, 6, 9, 13, 19, 28, 42, 63, 94, 141 …
g++ 輸出:1, 2, 4, 8, 16, 32, 64, 128 …

結論:不要迷信“2 倍”這個說法。編寫可移植代碼時,不要假設擴容因子。需要高效時,主動使用 reserve

三、增刪查改操作

函數(shù)作用
push_back(val)尾插
pop_back()尾刪
insert(pos, val)在迭代器 pos 前插入 val
erase(pos)刪除迭代器 pos 處的元素
find(begin, end, val)算法中的查找,不是 vector 成員
swap(vec)交換兩個 vector 的數(shù)據(jù)
operator[]隨機訪問

示例:

vector<int> v = {1, 2, 3};
v.push_back(4);           // 1 2 3 4
v.pop_back();             // 1 2 3
v.insert(v.begin(), 0);   // 0 1 2 3
v.erase(v.begin() + 1);   // 0 2 3

四、迭代器失效 —— 最容易踩的坑

迭代器本質(zhì)是一個指針(或封裝后的指針),指向容器中的某個元素。當容器的內(nèi)存布局發(fā)生變化時,舊的迭代器可能指向無效內(nèi)存,稱為迭代器失效。

4.1 會導致擴容的操作(insert、push_back、reserve、resize、assign)

這些操作可能重新分配內(nèi)存,使得原有的迭代器全部失效。

vector<int> v{1,2,3};
auto it = v.begin();
v.reserve(100);      // 擴容,it 失效
// 此時 it 已經(jīng)不安全,不能再使用
while (it != v.end()) { // 錯誤!可能崩潰
    cout << *it;
}

正確做法:在可能導致擴容的操作后,重新獲取迭代器。

it = v.begin();      // 重新賦值

4.2 erase 導致的失效

erase 刪除元素后,被刪除元素及其之后的所有迭代器都會失效(因為元素發(fā)生了移動)。典型的錯誤寫法:

vector<int> v{1,2,3,4};
auto it = v.begin();
while (it != v.end()) {
    if (*it % 2 == 0)
        v.erase(it);   // 錯誤:erase 后 it 失效,再 ++it 就是野指針
    ++it;
}

正確寫法:利用 erase 返回下一個有效迭代器。

while (it != v.end()) 
{ if (*it % 2 == 0) it = v.erase(it); 
// erase 返回被刪除元素的下一個位置 else ++it;
 }

4.3 Linux (g++) 與 VS 的差異

  • VS 對迭代器失效非常敏感,一旦使用失效迭代器,大概率立即崩潰(調(diào)試模式下會斷言)。
  • g++ 則相對寬容,擴容后舊迭代器可能仍指向原內(nèi)存(但已被釋放),程序可能“看起來正常”,實則存在隱患,例如輸出亂碼或段錯誤。

建議:統(tǒng)一按照“任何修改容量的操作都會導致迭代器失效”來編程,不要依賴編譯器行為。

五、OJ 實戰(zhàn):鞏固 vector 使用

5.1 只出現(xiàn)一次的數(shù)字(異或法)

int singleNumber(vector<int>& nums) {
    int ret = 0;
    for (auto e : nums) ret ^= e;
    return ret;
}

5.2 楊輝三角(vector<vector<int>>)

vector<vector<int>> generate(int numRows) {
    vector<vector<int>> vv(numRows);
    for (int i = 0; i < numRows; ++i) {
        vv[i].resize(i + 1, 1);
    }
    for (int i = 2; i < numRows; ++i) {
        for (int j = 1; j < i; ++j) {
            vv[i][j] = vv[i-1][j] + vv[i-1][j-1];
        }
    }
    return vv;
}

練習推薦:

  • 刪除排序數(shù)組中的重復項
  • 數(shù)組中出現(xiàn)次數(shù)超過一半的數(shù)字
  • 電話號碼的字母組合

六、模擬實現(xiàn) vector:核心框架

為了深入理解 vector,我們嘗試自己實現(xiàn)一個簡化版,命名為 bit::vector

6.1 成員變量與基本接口

namespace bit {
    template<class T>
    class vector {
    public:
        // 迭代器就是原生指針
        typedef T* iterator;
        typedef const T* const_iterator;
        // 構造、析構
        vector() : _start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {}
        vector(int n, const T& val = T()) : _start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {
            reserve(n);
            for (int i = 0; i < n; ++i)
                push_back(val);
        }
        ~vector() {
            delete[] _start;
            _start = _finish = _end_of_storage = nullptr;
        }
        // 迭代器
        iterator begin() { return _start; }
        iterator end() { return _finish; }
        const_iterator begin() const { return _start; }
        const_iterator end() const { return _finish; }
        // 容量
        size_t size() const { return _finish - _start; }
        size_t capacity() const { return _end_of_storage - _start; }
        bool empty() const { return _start == _finish; }
        // 元素訪問
        T& operator[](size_t pos) { return _start[pos]; }
        const T& operator[](size_t pos) const { return _start[pos]; }
        // 修改
        void push_back(const T& val);
        void pop_back();
        void reserve(size_t n);
        void resize(size_t n, const T& val = T());
        iterator insert(iterator pos, const T& val);
        iterator erase(iterator pos);
    private:
        iterator _start;          // 指向數(shù)據(jù)起始
        iterator _finish;         // 指向最后一個有效數(shù)據(jù)的下一個位置
        iterator _end_of_storage; // 指向已分配內(nèi)存的末尾
    };
}

6.2 核心實現(xiàn):reserve 與 push_back

template<class T>
void vector<T>::reserve(size_t n) {
    if (n > capacity()) {
        size_t old_size = size();
        T* new_data = new T[n];
        if (_start) {
            // 拷貝舊數(shù)據(jù)
            for (size_t i = 0; i < old_size; ++i)
                new_data[i] = _start[i];
            delete[] _start;
        }
        _start = new_data;
        _finish = _start + old_size;
        _end_of_storage = _start + n;
    }
}
template<class T>
void vector<T>::push_back(const T& val) {
    if (_finish == _end_of_storage) {
        size_t new_cap = capacity() == 0 ? 1 : capacity() * 2;
        reserve(new_cap);
    }
    *_finish = val;
    ++_finish;
}

6.3 深拷貝與 insert/erase

template<class T>
typename vector<T>::iterator vector<T>::insert(iterator pos, const T& val) {
    // 判斷擴容
    if (_finish == _end_of_storage) {
        size_t offset = pos - _start;
        reserve(capacity() == 0 ? 1 : capacity() * 2);
        pos = _start + offset;   // 更新 pos,因為擴容后原迭代器失效
    }
    // 后移元素
    for (iterator it = _finish; it > pos; --it)
        *it = *(it - 1);
    *pos = val;
    ++_finish;
    return pos;
}
template<class T>
typename vector<T>::iterator vector<T>::erase(iterator pos) {
    for (iterator it = pos; it < _finish - 1; ++it)
        *it = *(it + 1);
    --_finish;
    return pos;   // 返回被刪除元素的下一個位置
}

七、致命陷阱:memcpy 淺拷貝問題

在模擬實現(xiàn) reserve 時,如果用 memcpy 進行內(nèi)存復制會怎樣?

// 錯誤示范
void reserve(size_t n) {
    if (n > capacity()) {
        T* tmp = new T[n];
        if (_start) {
            memcpy(tmp, _start, size() * sizeof(T));  // 危險!
            delete[] _start;
        }
        _start = tmp;
        // ...
    }
}

問題
如果 T 是 string 或其他管理資源的類型(如 vector<int>),memcpy 只是按字節(jié)復制指針(淺拷貝),導致兩個對象指向同一塊堆內(nèi)存。當舊對象被 delete[] 時,會析構每個元素,釋放資源;而新對象中的元素仍持有已釋放的指針,最終導致雙重釋放內(nèi)存泄漏

正確做法:使用賦值操作(深拷貝)。

for (size_t i = 0; i < old_size; ++i)
    tmp[i] = _start[i];   // 調(diào)用 T 的拷貝賦值,實現(xiàn)深拷貝

因此,在編寫通用容器時,絕不能使用 memcpy 處理非 POD 類型。

八、動態(tài)二維數(shù)組:vector<vector<T>>

楊輝三角的代碼展示了 vector 的嵌套使用。物理上,外層 vector 的每個元素又是一個內(nèi)層 vector,它們的內(nèi)存不一定是連續(xù)的,但每個內(nèi)層 vector 內(nèi)部連續(xù)。

vector<vector<int>> vv(5);   // 5 行
for (int i = 0; i < 5; ++i)
    vv[i].resize(i+1, 1);    // 每行長度 i+1,初始化 1

這種結構比 C 語言的“指針數(shù)組”更安全、更易用。

九、總結與建議

  • 優(yōu)先使用 vector:動態(tài)數(shù)組是絕大多數(shù)場景的最佳選擇。
  • 善用 reserve:提前分配空間,避免頻繁擴容。
  • 警惕迭代器失效:任何可能改變?nèi)萘康牟僮骱?,原來持有的迭代器都可能失效,務必重新獲取。
  • 模擬實現(xiàn)是提升內(nèi)功的最佳途徑:親手實現(xiàn) reserve、push_back、insert、erase,你會對深拷貝、淺拷貝、異常安全有更深理解。
  • 不要用 memcpy 拷貝非 POD 元素:始終使用賦值或拷貝構造。

vector 的用法看似簡單,但其中的陷阱和原理值得每個 C++ 開發(fā)者反復琢磨。希望這篇文章能幫你徹底掌握 vector,并在面試和工程中游刃有余。

練習題推薦

  • LeetCode 26. 刪除有序數(shù)組中的重復項
  • LeetCode 118. 楊輝三角
  • LeetCode 17. 電話號碼的字母組合

下一篇預告:我們將深入 list 容器,對比 vector 與 list 的優(yōu)劣,并探討迭代器失效在不同容器中的表現(xiàn)。敬請期待!

到此這篇關于C++ vector從入門到模擬實現(xiàn)的文章就介紹到這了,更多相關C++ vector實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論

西丰县| 夏河县| 祁东县| 临沂市| 阿克陶县| 唐河县| 彰化市| 五河县| 鄂伦春自治旗| 清涧县| 图片| 邢台市| 兴业县| 县级市| 夏邑县| 孙吴县| 察雅县| 乌审旗| 永兴县| 图木舒克市| 蛟河市| 紫云| 繁昌县| 台江县| 沙坪坝区| 嘉兴市| 永福县| 兰州市| 宁乡县| 高淳县| 东莞市| 桦南县| 砀山县| 南康市| 夏邑县| 台州市| 溧水县| 邢台县| 本溪| 贵定县| 樟树市|