跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ vector 全面解析:从基础用法到深度剖析

C++ vector 是 STL 中最常用的动态数组容器,支持随机访问和自动扩容。详细讲解了 vector 的构造方法、迭代器使用规则、空间管理(size/capacity)、元素增删查改操作以及底层内存模拟实现。同时结合经典算法题目(如异或求唯一数、电话号码组合等)展示了 vector 在实际开发中的应用技巧及注意事项,包括迭代器失效处理、边界检查机制及深拷贝浅拷贝区别。

宁静发布于 2026/3/28更新于 2026/9/459 浏览
C++ vector 全面解析:从基础用法到深度剖析

前言

在 C++ 标准模板库(STL)中,vector 是最常用也最灵活的容器之一。它作为动态数组,既保留了数组随机访问的高效性,又具备动态扩容的灵活性,在实际开发中有着广泛的应用。

本文将从 vector 的构造方法入手,逐步深入到迭代器使用、空间管理、元素操作等核心知识点,讲解各种 API 的用法细节,并通过模拟实现代码帮助读者理解其底层工作原理。针对 vector 使用中常见的迭代器失效、边界访问等问题进行详细说明,并结合典型算法题目展示 vector 在实际场景中的应用。

构造 vector 的几种方法

vector 支持多种构造方式,例如指定初始大小和值,或通过迭代器区间初始化。

vector<int> v1(10, 1);
vector<int> v3(v1.begin(), v1.end());

对于嵌套 vector,需注意 resize 时的层级关系:

vector<vector<int>> vv;
vv.resize(n);
for(auto& row : vv) row.resize(m);

vector 的迭代器

  • begin: 获取第一个元素的迭代器
  • end: 获取最后一个元素的下一个位置的迭代器
  • rbegin: 获取最后一个元素的反向迭代器
  • rend: 获取第一个元素的上一个位置的反向迭代器

注意:vector 的迭代器不一定是原生指针;反向迭代器 ++ 和迭代器 ++ 的移动方向相反;范围 for 循环配合反向迭代器可倒序遍历。

vector 的空间操作

  • size: 获取数据个数
  • capacity: 获取容量大小
  • empty: 判断是否为空
  • resize: 改变 vector 的 size 和 capacity
  • reserve: 仅改变 vector 的 capacity

注意:使用 [] 访问时依据 size 判断越界。若 capacity 为 10 而 size 为 1,访问索引 9 会报错。

vector 获取位置

  • operator[]: 一般用于访问
  • front: 返回容器中第一个元素的引用
  • back: 返回容器最后一个元素的引用
  • data (C++11): 获取 vector 内部存储元素的连续内存的首地址

vector 的增删查改

  • push_back: 尾插
  • pop_back: 尾删
  • insert: 插入元素
  • erase: 删除指定位置的数据,返回指向被删除元素下一个元素的迭代器
  • swap: 交换两个 vector 的数据空间
  • emplace: 原地构造元素(涉及右值引用)

注意:vector 本身没有 find 成员函数,但可使用 std::find 算法查找。区间操作通常遵循左闭右开原则。

vector 的模拟实现

设计思路

  1. 通过成员变量名称猜测作用。
  2. 通过构造函数和插入接口分析成员用途。
  3. 先构建整体框架。

核心代码

namespace renshen {
template<class T>
class vector {
public:
    typedef T* iterator;
    typedef const T* const_iterator;

    iterator begin() { return _start; }
    iterator end() { return _finish; }
    const_iterator begin() const { return _start; }
    const_iterator end() const { return _finish; }

    vector(size_t n, const T& val = T()) 
        : _start(nullptr), _finish(nullptr), _endofstorage(nullptr) {
        resize(n, val);
    }

    template<class InputIterator>
    vector(InputIterator first, InputIterator last)
        : _start(nullptr), _finish(nullptr), _endofstorage(nullptr) {
        while(first != last) {
            push_back(*first);
            ++first;
        }
    }

    vector() : _start(nullptr), _finish(nullptr), _endofstorage(nullptr) {}

    vector(const vector<T>& v) 
        : _start(nullptr), _finish(nullptr), _endofstorage(nullptr) {
        _start = new T[v.capacity()];
        for(size_t i = 0; i < v.size(); i++) {
            _start[i] = v._start[i];
        }
        _finish = _start + v.size();
        _endofstorage = _start + v.capacity();
    }

    void swap(vector<T>& v) {
        std::swap(_start, v._start);
        std::swap(_finish, v._finish);
        std::swap(_endofstorage, v._endofstorage);
    }

    vector<T>& operator=(vector<T> v) {
        swap(v);
        return *this;
    }

    ~vector() {
        if(_start) {
            delete[] _start;
            _start = _finish = _endofstorage = nullptr;
        }
    }

    void reserve(size_t n) {
        if(n > capacity()) {
            size_t sz = size();
            T* tmp = new T[n];
            if(_start) {
                for(size_t i = 0; i < sz; i++) {
                    tmp[i] = _start[i];
                }
                delete[] _start;
            }
            _start = tmp;
            _finish = _start + sz;
            _endofstorage = _start + n;
        }
    }

    void resize(size_t n, const T& val = T()) {
        if(n < size()) {
            _finish = _start + n;
        } else {
            reserve(n);
            while(_finish != _start + n) {
                *_finish = val;
                ++_finish;
            }
        }
    }

    void push_back(const T& x) {
        insert(end(), x);
    }

    void pop_back() {
        erase(--end());
    }

    size_t capacity() const { return _endofstorage - _start; }
    size_t size() const { return _finish - _start; }

    T& operator[](size_t pos) {
        assert(pos < size());
        return _start[pos];
    }

    const T& operator[](size_t pos) const {
        assert(pos < size());
        return _start[pos];
    }

    iterator insert(iterator pos, const T& x) {
        assert(pos >= _start && pos <= _finish);
        if(_finish == _endofstorage) {
            size_t len = pos - _start;
            size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
            reserve(newcapacity);
            pos = _start + len;
        }
        iterator end = _finish - 1;
        while(end >= pos) {
            *(end + 1) = *end;
            --end;
        }
        *pos = x;
        ++_finish;
        return pos;
    }

    iterator erase(iterator pos) {
        assert(pos >= _start && pos < _finish);
        iterator it = pos + 1;
        while(it != _finish) {
            *(it - 1) = *it;
            ++it;
        }
        --_finish;
        return pos;
    }

private:
    iterator _start;
    iterator _finish; // 最后一个元素的下一个位置
    iterator _endofstorage; // 内存的末尾的下一个位置
};

void print(const vector<int>& v) {
    for(auto e : v) {
        cout << e << " ";
    }
    cout << endl;
}
}

重载与类型转换

  • vector v(10, 1);:编译器调用 size_t 构造函数,因为第二个参数 1 匹配 val 类型,且 10 默认为 int 但会被隐式转换为 size_t。
  • vector v1(10, "1111");:调用 size_t 构造函数。

注意事项

  1. 迭代器失效:insert 后若发生扩容,原迭代器可能失效成为野指针。erase 和 insert 的返回值是指向新位置的迭代器,应优先使用返回值。
  2. 通用性:代码应尽量兼容不同编译器环境,避免依赖特定编译器特性。
  3. 拷贝语义:reverse 和构造函数中慎用 memcpy,遇到非 POD 类型(如 string)会导致浅拷贝错误,应使用循环赋值。
  4. 默认值:int j = int(); 等价于 int j = 0;。

典型应用示例

只出现一次的数字

利用异或运算性质(相同为 0,不同为 1,符合分配律),对数组所有元素异或,结果即为只出现一次的数字。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int e = 0;
        for(int i = 0; i < nums.size(); i++) {
            e = e ^ nums[i];
        }
        return e;
    }
};

边界检查机制

在 Debug 模式下,std::vector::at 总是做边界检查,而 operator[] 不做边界检查。两者行为差异需根据需求选择。

容量与大小变化

  • 容量(capacity)不会变小,resize 减小大小时仅修改 size 指针。
  • reserve 增大容量时不改变 size。
int main() {
    int ar[] = {1,2,3,4,5,6,7,8,9,10};
    int n = sizeof(ar)/sizeof(int);
    vector<int> v(ar, ar+n);
    cout << v.size() << ":" << v.capacity() << endl; // 10:10
    v.reserve(100);
    v.resize(20);
    cout << v.size() << ":" << v.capacity() << endl; // 20:100
    v.reserve(50);
    v.resize(5);
    cout << v.size() << ":" << v.capacity() << endl; // 5:100
    return 0;
}

电话号码的字母组合

使用 DFS 回溯法,注意 vector 为空时直接返回空 vector 而非空字符串,传递 vector 引用避免重复拷贝。

class Solution {
public:
    string qq[10] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    void dfs(vector<string>& f, int level, string p, string digits) {
        if(level == digits.size()) {
            f.push_back(p);
            return;
        }
        for(int i = 0; i < qq[digits[level]-'0'].size(); i++) {
            dfs(f, level+1, p+qq[digits[level]-'0'][i], digits);
        }
    }
    vector<string> letterCombinations(string digits) {
        vector<string> a;
        if(digits == "") return a;
        dfs(a, 0, "", digits);
        return a;
    }
};

数组中出现次数超过一半的数字

排序后取中间元素即可。

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int>& numbers) {
        sort(numbers.begin(), numbers.end());
        return numbers[numbers.size()/2];
    }
};

只出现一次的数 II

统计二进制位出现次数,对 3 取模,剩余即为目标数字的二进制位。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for(int i = 0; i < 32; ++i) {
            int total = 0;
            for(int e : nums) {
                total += ((e >> i) & 1);
            }
            if(total % 3) {
                ret |= (1 << i);
            }
        }
        return ret;
    }
};

只出现一次的数 III

全部异或得到 sum,找出最低位 1,将数组分为两组分别异或。

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        long long int sum = 0;
        for(auto e : nums) {
            sum = sum ^ e;
        }
        long long int lowbit = sum & (-sum);
        int num1 = 0, num2 = 0;
        for(auto e : nums) {
            if((lowbit & e) == 0) num1 ^= e;
            else num2 ^= e;
        }
        return {num1, num2};
    }
};

目录

  1. 前言
  2. 构造 vector 的几种方法
  3. vector 的迭代器
  4. vector 的空间操作
  5. vector 获取位置
  6. vector 的增删查改
  7. vector 的模拟实现
  8. 设计思路
  9. 核心代码
  10. 重载与类型转换
  11. 注意事项
  12. 典型应用示例
  13. 只出现一次的数字
  14. 边界检查机制
  15. 容量与大小变化
  16. 电话号码的字母组合
  17. 数组中出现次数超过一半的数字
  18. 只出现一次的数 II
  19. 只出现一次的数 III

更多推荐文章

查看全部
  • 栈结构在算法题中的五种经典应用:去重、退格、计算、解码与验证
  • UniApp 微信小程序多商家助农农产品商城系统架构
  • AI 大模型应用场景落地策略:从理论到实践
  • Redis Set 数据类型 C++ 实战指南
  • DeepSeek 团队架构分析:清北应届生主导大模型研发
  • AIGC 赋能 Java 编程:智能工具提升开发效率与质量
  • Golang 后端性能优化手册:高级优化技巧
  • Qwen-Image AI 绘画:ComfyUI 镜像部署与使用指南
  • C++ 继承:面向对象代码复用的核心机制
  • Flutter 三方库 flutter_dropzone 的鸿蒙化适配指南
  • Vue 下拉刷新组件开发实战与 Slot 用法详解
  • Python 爬虫抓取小说并保存为 TXT 文件教程
  • Git 新建分支首次推送到远程仓库的操作指南
  • CCF-GESP 2025 年 9 月四级 C++ 真题解析:排兵布阵
  • Git 分支管理完全指南:从基础到团队协作
  • FPGA 原型验证入门:Simulation 与 Emulation 的区别
  • 最佳信号覆盖问题
  • 春节寒假作业辅导:基于 Rokid 灵珠平台打造 AI Glasses 作业助手
  • PyQt5 超详细入门教程:基础与常用控件
  • Whisper-large-v3 语音识别模型部署与会议转录实测

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online