无奈无赖 發表於 2026-1-7 10:18:00

C++ vector使用以及底层核心剖析

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>一、vector 基础使用</li><ul class="second_class_ul"><li>1. 头文件与初始化</li><li>2. 三种遍历方式</li><li>3. 插入与删除</li><li>4. 二维 vector(二维数组)</li><li>5. 实用案例:杨辉三角</li></ul><li>二、vector 核心接口对比与细节</li><ul class="second_class_ul"><li>1. reserve:仅扩容,不初始化</li><li>2. resize:改变元素个数,按需初始化</li><li>3. 不支持重载 &lt;&lt;和&gt;&gt;</li></ul><li>三、迭代器失效</li><ul class="second_class_ul"><li>1. 扩容导致的迭代器失效(野指针)</li><li>2. 插入 / 删除导致的迭代器语义失效</li><ul class="third_class_ul"><li>(1)insert 后迭代器语义失效</li></ul><li>(2)erase 后迭代器语义失效</li><ul class="third_class_ul"></ul></ul><li>四、vector 底层实现关键细节</li><ul class="second_class_ul"><li>1. 迭代器区间构造(模板复用)</li><ul class="third_class_ul"></ul><li>2. 浅拷贝问题修复</li><ul class="third_class_ul"></ul><li>3. 内置类型的 &ldquo;伪构造函数&rdquo;</li><ul class="third_class_ul"></ul><li>4. const_iterator 的 typename 修饰</li><ul class="third_class_ul"></ul></ul></ul></div><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在 C++ 标准库中,vector 是最常用的容器之一,它本质上是一个动态顺序表,兼具数组的随机访问特性和动态扩容的灵活性。本文将从基础使用、核心接口、迭代器失效、底层实现等维度,全面拆解 vector 的核心知识点,帮你彻底吃透这个容器。</p>
<p class="maodian"></p><h2>一、vector 基础使用</h2>
<p class="maodian"></p><h3>1. 头文件与初始化</h3>
<p>使用 vector 首先需要包含头文件<code>&lt;vector&gt;</code>,它的初始化方式灵活多样,适配不同场景:</p>
<div class="jb51code"><pre class="brush:cpp;">#include &lt;iostream&gt;
#include &lt;vector&gt;
using namespace std;
void test_vector_init() {
    // 1. 空vector
    vector&lt;int&gt; v1;
    // 2. 初始化10个元素,每个元素值为1
    vector&lt;int&gt; v2(10, 1);
    // 3. 用v2的迭代器区间初始化v3(拷贝v2所有元素)
    vector&lt;int&gt; v3(v2.begin(), v2.end());
    // 4. 列表初始化(C++11及以上)
    vector&lt;int&gt; v4{1, 2, 0};
}</pre></div>
<p class="maodian"></p><h3>2. 三种遍历方式</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vector 的遍历和 string 高度相似,支持下标、迭代器、范围 for 三种方式:</p>
<div class="jb51code"><pre class="brush:cpp;">void test_vector_traverse() {
    vector&lt;int&gt; v{1, 2, 3, 4, 5};
    // 方式1:下标遍历(随机访问特性)
    for (size_t i = 0; i &lt; v.size(); i++) {
      cout &lt;&lt; v &lt;&lt; " ";
    }
    cout &lt;&lt; endl;
    // 方式2:迭代器遍历
    vector&lt;int&gt;::iterator it = v.begin();
    while (it != v.end()) {
      cout &lt;&lt; *it &lt;&lt; " ";
      ++it;
    }
    cout &lt;&lt; endl;
    // 方式3:范围for(C++11及以上)
    for (auto e : v) {
      cout &lt;&lt; e &lt;&lt; " ";
    }
    cout &lt;&lt; endl;
}</pre></div>
<p class="maodian"></p><h3>3. 插入与删除</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vector 的插入(insert)和删除(erase)是高频操作,需注意迭代器的使用规则:</p>
<div class="jb51code"><pre class="brush:cpp;">void test_vector_insert_erase() {
    vector&lt;int&gt; v(10, 1);
    // 尾插元素2
    v.push_back(2);
    // 头插元素3(insert仅支持迭代器传参)
    v.insert(v.begin(), 3);
    // 在第5个位置前插入4(迭代器偏移)
    v.insert(v.begin() + 5, 4);
    for (auto e : v) {
      cout &lt;&lt; e &lt;&lt; " ";        // 输出:3 1 1 1 1 4 1 1 1 1 1 1 2
    }
    cout &lt;&lt; endl;
    // 尾删元素
    v.pop_back();
    // 删除第5个位置的元素
    v.erase(v.begin() + 5);
    for (auto e : v) {
      cout &lt;&lt; e &lt;&lt; " ";        // 输出:3 1 1 1 1 1 1 1 1 1 1
    }
    cout &lt;&lt; endl;
}</pre></div>
<p class="maodian"></p><h3>4. 二维 vector(二维数组)</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vector 支持嵌套定义实现二维数组,相比原生二维数组更灵活(每行长度可不同):</p>
<div class="jb51code"><pre class="brush:cpp;">void test_vector_2d() {
    // 初始化10行5列的二维数组,每个元素初始值为1
    vector&lt;int&gt; v(5, 1);
    vector&lt;vector&lt;int&gt;&gt; vv(10, v);
    // 修改第3行第2列的元素(下标从0开始)
    vv = 2;
    // 遍历二维vector
    for (size_t i = 0; i &lt; vv.size(); i++) {
      for (size_t j = 0; j &lt; vv.size(); j++) {
            cout &lt;&lt; vv &lt;&lt; " ";
      }
      cout &lt;&lt; endl;
    }
}</pre></div>
<p class="maodian"></p><h3>5. 实用案例:杨辉三角</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;结合 vector 的初始化、resize、元素访问等特性,实现 LeetCode 经典题 &ldquo;杨辉三角&rdquo;:</p>
<div class="jb51code"><pre class="brush:cpp;">class Solution {
public:
    vector&lt;vector&lt;int&gt;&gt; generate(int numRows) {
      // 初始化numRows行的二维vector
      vector&lt;vector&lt;int&gt;&gt; vv(numRows);
      for(size_t i = 0; i &lt; numRows; i++) {
            // 第i行开辟i+1个空间,初始值为0
            vv.resize(i+1, 0);
            // 每行首尾元素设为1
            vv.front() = vv.back() = 1;
      }
      // 填充中间元素
      for(int i = 1; i &lt; vv.size(); i++) {
            for(int j = 1; j &lt; vv.size()-1; j++) {
                vv = vv + vv;
            }
      }
      return vv;
    }
};</pre></div>
<p class="maodian"></p><h2>二、vector 核心接口对比与细节</h2>
<p class="maodian"></p><h3>1. reserve:仅扩容,不初始化</h3>
<p>vector 的<code>reserve</code>和 string 的<code>reserve</code>存在关键区别:</p>
<ul><li>当<code>n &lt; size()</code>时,vector 的 reserve<strong>无任何操作</strong>;</li><li>string 的 reserve 若缩容(多数编译器忽略),至少保证容量不低于 size ()。</li></ul>
<p class="maodian"></p><h3>2. resize:改变元素个数,按需初始化</h3>
<p><code>resize</code>会直接修改 vector 的元素个数,行为分三种情况:</p>
<ul><li><code>n &lt; size()</code>:删除末尾元素,仅调整有效元素个数;</li><li><code>size() &lt; n &lt; capacity()</code>:在末尾插入元素,用默认值初始化;</li><li><code>n &gt; capacity()</code>:先扩容,再插入元素初始化。</li></ul>
<p><strong>注意</strong>:vector 默认按元素类型的默认值初始化(如 int 为 0),而 string 固定初始化为<code>&#39;\0&#39;</code>。</p>
<div class="jb51code"><pre class="brush:cpp;">// resize接口原型(可指定初始化值)
void resize (size_type n, value_type val = value_type());</pre></div>
<p class="maodian"></p><h3>3. 不支持重载 &lt;&lt;和&gt;&gt;</h3>
<p>vector 没有默认的输入输出重载,需手动实现输入输出逻辑:</p>
<div class="jb51code"><pre class="brush:cpp;">// 单个元素输入
vector&lt;int&gt; v;
int x;
cin &gt;&gt; x;
v.push_back(x);
// 多个元素输入(初始化5个元素)
vector&lt;int&gt; v1(5, 0);
for (size_t i = 0; i &lt; 5; i++) {
    cin &gt;&gt; v1;
}</pre></div>
<p class="maodian"></p><h2>三、迭代器失效</h2>
<p>迭代器失效是 vector 使用中最容易出错的地方,本质是<strong>迭代器指向的空间失效</strong>或<strong>指向位置的语义改变</strong>,主要分为两类场景:</p>
<p class="maodian"></p><h3>1. 扩容导致的迭代器失效(野指针)</h3>
<p>当 vector 插入元素触发扩容时,原空间会被释放,之前的迭代器变为野指针:</p>
<div class="jb51code"><pre class="brush:cpp;">// 错误示例:扩容后迭代器失效
void test_vector_invalid1() {
    vector&lt;int&gt; v{1,2,3,4};
    // 插入位置迭代器
    auto pos = v.begin() + 1;
    // 插入元素触发扩容,原pos指向的空间被释放
    v.insert(pos, 5);
    // 访问失效迭代器,结果未定义
    cout &lt;&lt; *pos &lt;&lt; endl;
}</pre></div>
<p><strong>解决方法</strong>:插入时记录迭代器相对位置,扩容后重新修正迭代器:</p>
<div class="jb51code"><pre class="brush:cpp;">iterator insert(iterator pos, const T&amp; x) {
    assert(pos &gt;= _start &amp;&amp; pos &lt;= _finish);
    if (_finish == _end_of_storage) {
      // 记录相对位置
      size_t len = pos - _start;
      // 扩容
      reserve(capacity() == 0 ? 4 : 2 * capacity());
      // 修正迭代器指向新空间
      pos = _start + len;
    }
    // 元素后移 + 插入新元素
    iterator end = _finish - 1;
    while (end &gt;= pos) {
      *(end + 1) = *end;
      --end;
    }
    *pos = x;
    ++_finish;
    // 返回新插入元素的迭代器
    return pos;
}</pre></div>
<p class="maodian"></p><h3>2. 插入 / 删除导致的迭代器语义失效</h3>
<p class="maodian"></p><h4>(1)insert 后迭代器语义失效</h4>
<p>insert 会挪动元素,原迭代器指向的位置语义改变(不再是原来的元素):</p>
<div class="jb51code"><pre class="brush:cpp;">void test_vector_invalid2() {
    vector&lt;int&gt; v{1,5,2,3,4};
    auto p = find(v.begin(), v.end(), 2);
    if (p != v.end()) {
      // 插入后p不再指向2,而是指向新插入的40
      v.insert(p, 40);
      // 错误:修改的是40,而非原来的2
      (*p) *= 10;
    }
}</pre></div>
<p><strong>解决方法</strong>:接收 insert 返回的新迭代器,重新定位:</p>
<div class="jb51code"><pre class="brush:cpp;">auto p = find(v.begin(), v.end(), 2);
if (p != v.end()) {
    // 接收新迭代器,指向插入的40
    p = v.insert(p, 40);
    // 修改原来的2(p+1位置)
    (*(p + 1)) *= 10;
}</pre></div>
<p class="maodian"></p><h3>(2)erase 后迭代器语义失效</h3>
<p><strong>erase 删除元素后,原迭代器指向的位置变为下一个元素</strong>,若直接 ++ 会跳过元素或死循环:</p>
<div class="jb51code"><pre class="brush:cpp;">// 错误示例:删除所有偶数,迭代器失效导致漏删/死循环
void test_vector_invalid3() {
    vector&lt;int&gt; v{1,2,3,4};
    auto it = v.begin();
    while (it != v.end()) {
      if (*it % 2 == 0) {
            v.erase(it); // 删除后it失效
      }
      ++it; // 失效迭代器++,行为未定义
    }
}</pre></div>
<p><strong>解决方法</strong>:接收 erase 返回的迭代器(指向删除元素的下一个位置):</p>
<div class="jb51code"><pre class="brush:cpp;">// 正确版本:删除所有偶数
void test_vector_erase_fix() {
    vector&lt;int&gt; v{1,2,3,4};
    auto it = v.begin();
    while (it != v.end()) {
      if (*it % 2 == 0) {
            // 接收返回值,更新迭代器
            it = v.erase(it);
      } else {
            ++it; // 仅非删除场景++
      }
    }
    // 输出:1 3
    for (auto e : v) cout &lt;&lt; e &lt;&lt; " ";
}</pre></div>
<p class="maodian"></p><h2>四、vector 底层实现关键细节</h2>
<p class="maodian"></p><h3>1. 迭代器区间构造(模板复用)</h3>
<p>vector 的迭代器区间构造函数设计为模板函数,支持任意容器的迭代器初始化(只要类型匹配):</p>
<div class="jb51code"><pre class="brush:cpp;">// 类模板的成员函数可嵌套函数模板
template &lt;class InputIterator&gt;
vector(InputIterator first, InputIterator last) {
    while (first != last) {
      push_back(*first);
      ++first;
    }
}
// 用法:仅拷贝v1的前3个元素
vector&lt;int&gt; v1{1,2,3,4,5};
vector&lt;int&gt; v2(v1.begin(), v1.begin()+3);</pre></div>
<p class="maodian"></p><h3>2. 浅拷贝问题修复</h3>
<p>vector 扩容时若用 memcpy 拷贝元素,会导致自定义类型(如 string)的浅拷贝问题,需改用赋值运算符:</p>
<div class="jb51code"><pre class="brush:cpp;">void reserve(size_t n) {
    if (n &gt; capacity()) {
      size_t old_size = size();
      T* tmp = new T;
      // 错误:memcpy是浅拷贝,自定义类型会析构重复释放
      // memcpy(tmp, _start, size() * sizeof(T));
      // 正确:调用T的赋值运算符,深拷贝自定义类型
      for (size_t i = 0; i &lt; old_size; i++) {
            tmp = _start;
      }
      delete[] _start;
      _start = tmp;
      _finish = _start + old_size;
      _end_of_storage = _start + n;
    }
}</pre></div>
<p class="maodian"></p><h3>3. 内置类型的 &ldquo;伪构造函数&rdquo;</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vector 的 resize 接口默认参数为<code>T()</code>,为了适配内置类型(如 int),C++ 编译器会为内置类型模拟构造函数:</p>
<div class="jb51code"><pre class="brush:cpp;">void resize(size_t n, T val = T()) {
    if (n &lt; size()) {
      _finish = _start + n;
    } else {
      reserve(n);
      while (_finish &lt; _start + n) {
            *_finish = val;
            ++_finish;
      }
    }
}
// 调用示例:int()等价于0,double()等价于0.0
v.resize(10); // 内置类型用默认值初始化</pre></div>
<p class="maodian"></p><h3>4. const_iterator 的 typename 修饰</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在模板中使用<code>vector&lt;T&gt;::const_iterator</code>时,需加<code>typename</code>说明这是类型(否则编译器视为静态成员):</p>
<div class="jb51code"><pre class="brush:cpp;">// 错误:编译器无法区分const_iterator是类型还是成员
// vector&lt;T&gt;::const_iterator it = v.begin();
// 正确1:加typename说明是类型
typename vector&lt;T&gt;::const_iterator it1 = v.begin();
// 正确2:用auto自动推导(推荐)
auto it2 = v.begin();</pre></div>
<p>到此这篇关于C++ vector:从使用到底层核心剖析的文章就介绍到这了,更多相关C++ vector用法内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>C++中std::vector的具体使用</li><li>C++  STL _ Vector使用及模拟实现</li><li>C++STL教程之vector模板的使用</li><li>C++中vector&lt;vector&lt;int&gt; &gt;的基本使用方法</li><li>C++ 基础函数的介绍及使用(Vector+deque+STL)</li><li>C++顺序容器(vector、deque、list)的使用详解</li><li>C++ 容器 Vector 的使用方法</li><li>C++入门之vector使用详解</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: C++ vector使用以及底层核心剖析