40 C++ STL模板库9-容器2-vector

C++ STL模板库9-容器2-vector

文章目录

  • [C++ STL模板库9-容器2-vector](#C++ STL模板库9-容器2-vector)
    • 一、基础概念
      • [1. 类型成员(Type Members)](#1. 类型成员(Type Members))
      • [2. 模板参数](#2. 模板参数)
    • 二、构造函数
      • [1. 语法](#1. 语法)
      • [2. 示例](#2. 示例)
    • 三、元素访问
      • [1. 函数说明](#1. 函数说明)
      • [2. 示例代码](#2. 示例代码)
    • 四、容量操作
      • [1. 函数说明](#1. 函数说明)
      • [2. 关键点说明](#2. 关键点说明)
      • [3. 关键操作解析](#3. 关键操作解析)
      • [4. 操作示例](#4. 操作示例)
    • 五、修改
      • [1. 函数说明](#1. 函数说明)
      • [2. 关键操作解析](#2. 关键操作解析)
      • [3. 操作示例](#3. 操作示例)
      • [4. 操作技巧](#4. 操作技巧)
    • 六、迭代器操作
      • [1. 函数说明](#1. 函数说明)
      • [2. 关键操作解析](#2. 关键操作解析)
      • [3. 操作示例](#3. 操作示例)
      • [4. 注意事项](#4. 注意事项)
    • 七、高效操作(C++11+)
      • [1. 函数说明](#1. 函数说明)
      • [2. 关键操作解析](#2. 关键操作解析)
      • [3. 常见错误规避](#3. 常见错误规避)
      • [4. 优先选择原则](#4. 优先选择原则)
    • 八、非成员函数
      • [1. 函数说明](#1. 函数说明)
      • [2. 示例代码](#2. 示例代码)
      • [3. 操作对比总结](#3. 操作对比总结)
    • 九、完整应用示例
      • [1. 类对象的向量](#1. 类对象的向量)
      • [2. 通过数组初始化向量](#2. 通过数组初始化向量)
    • 十、关键特性
    • 十一、应用注意事项⚖️
      • [1. 完美转发陷阱](#1. 完美转发陷阱)
      • [2. 高性能尾部操作](#2. 高性能尾部操作)
      • [3. 内存释放替代方案](#3. 内存释放替代方案)
      • [4. 函数选择指南](#4. 函数选择指南)
      • [5. 避免裸指针操作](#5. 避免裸指针操作)

vector- 动态数组,也叫向量,内存连续,动态管理内存,下标访问,尾部增删效率高。是最常用的容器。

一、基础概念

1. 类型成员(Type Members)

类型 说明
value_type 元素类型(如 int
allocator_type 分配器类型(默认 std::allocator<T>
size_type 大小类型(通常 size_t
reference 元素引用(value_type&
const_reference 常量引用(const value_type&
iterator 随机访问迭代器
const_iterator 常量迭代器

2. 模板参数

  • std::vector<T>(...) v; // T:元素类型, ...:参数

  • 示例:

    cpp 复制代码
    #include <vector>
    
    vector<int> vn;
    vector<string> vs;
    
    // 元素的预分配和初始化
    vector<int> vn (10); // 用0初始化10个元素
    vector<int> vn (10, 5); // 用5初始化10个元素

二、构造函数

1. 语法

cpp 复制代码
vector();                        // 默认构造 
vector(size_type count, T value); // 填充构造 
vector(iterator first, iterator last); // 范围构造 
vector(initializer_list<T> init);      // 初始化列表构造

2. 示例

cpp 复制代码
#include <iostream>
#include <vector>
#include <array>

int main() {
    // ========== 1. 默认构造 ==========
    std::vector<int> v1;  // 创建空vector 
    std::cout << "v1大小: " << v1.size()  << "\n";  // 输出 0

    // ========== 2. 填充构造 ==========
    std::vector<char> v2(5, 'A');  // 5个'A'
    std::cout << "v2内容: ";
    for (char c : v2) std::cout << c;               // 输出 AAAAA

    // ========== 3. 范围构造 ==========
    std::array<int, 3> arr{10, 20, 30};
    std::vector<int> v3(arr.begin(),  arr.end());   // 复制数组元素 
    
    std::cout << "\nv3内容: ";
    for (int n : v3) std::cout << n << " ";          // 输出 10 20 30 

    // ========== 4. 初始化列表构造 ==========
    std::vector<std::string> v4 = {"Apple", "Banana", "Cherry"};
    std::cout << "\nv4内容: ";
    for (auto& s : v4) std::cout << s << " ";          // 输出 Apple Banana Cherry

    // ========== 组合验证 ==========
    std::vector<int> v5{ 
        v3.begin(),  // 复用v3的范围构造 
        v3.begin()  + 2 
    };
    std::cout << "\nv5内容: ";
    for (int n : v5) std::cout << n << " ";          // 输出 10 20 

    return 0;
}
  • 初始化列表的等价操作

    cpp 复制代码
    // 以下两种写法等价
    std::vector<int> a = {1,2,3};
    std::vector<int> b({1,2,3});  // 显式构造函数 
  • 显式类型转换

    cpp 复制代码
    // 避免整数类型混淆 
    std::vector<int> v1(5, 2.5);    // 输出 {2,2,2,2,2}(截断)
    std::vector<double> v2{5, 2.5}; // 输出 {5.0, 2.5}(保留精度)

三、元素访问

1. 函数说明

函数 说明
at(size_type pos) 边界检查访问(越界抛异常)
operator[](size_type pos) 无检查直接访问
front() 首元素引用
back() 末元素引用
data() 底层数组指针

2. 示例代码

cpp 复制代码
#include <iostream>
#include <vector>
#include <stdexcept> // 异常处理

int main() {
    std::vector<int> nums{10, 20, 30, 40, 50};

    // ===== 1. at() 
    std::cout << "at(2): " << nums.at(2) << '\n';     // ✅ 输出 30 

    // ===== 2. operator[] 无检查访问 ===== 
    std::cout << "operator[3]: " << nums[3] << '\n';  // ✅ 输出 40 
    // nums[10] = 100; // ❌ 未定义行为(可能崩溃或数据污染)

    // ===== 3. front() & back() 端点访问 =====
    std::cout << "front(): " << nums.front() << '\n';  // ✅ 输出 10(首元素)
    std::cout << "back(): " << nums.back() << '\n';    // ✅ 输出 50(尾元素)

    // ===== 4. data() 底层指针访问 =====
    int* ptr = nums.data();
    std::cout << "data()[1]: " << ptr[1] << '\n';  // ✅ 输出 20 
    *ptr = 100;  // 修改首元素
    std::cout << "front()修改后: " << nums.front() << '\n';  // 输出 100

    return 0;
}
  • 优先选择原则:
    • 用户输入索引必须用at()安全
    • 内部循环索引访问用 operator[]高效
    • 和C兼容用data()兼容

四、容量操作

1. 函数说明

函数 作用描述 时间复杂度 内存影响
empty() 判断容器是否为空 O(1)
size() 获取当前元素数量 O(1)
max_size() 系统支持的最大元素数 O(1)
capacity() 当前预分配内存容量 O(1)
reserve(new_cap) 预扩容至指定容量 O(n) 可能重新分配内存
shrink_to_fit() 释放多余内存(非强制) O(n) 可能缩小容量

2. 关键点说明

  • 大小可增可减,引起大小变化的函数包括resize()/push_back()/pop_back()/insert()/erase()
  • 容量只增不减,直接引起容量变化的函数只有reserve()一个。
  • 大小的增加可能导致容量的增加,容量的变化不会引起大小的变化。
  • 通过resize()增加大小,新增部分会被初始化,但是通过reserve()增加容量,新增部分不做初始化。
  • 位于容量范围内但在大小范围外的元素,可以通过下标或迭代器访问,但其值不确定。
  • 无论是大小还是容量,它们的变化永远发生在向量容器的尾部。

3. 关键操作解析

  • 基础状态检测(empty()/size()

    cpp 复制代码
    std::vector<int> v;
    std::cout << "初始状态: " 
            << "empty=" << v.empty()   // 输出 true 
            << ", size=" << v.size()   // 输出 0 
            << "\n";
    
    v.push_back(10);
    std::cout << "添加元素后: "
            << "empty=" << v.empty()   // 输出 false 
            << ", size=" << v.size()   // 输出 1 
            << "\n";
  • 容量动态增长(capacity()/reserve()

    cpp 复制代码
    std::vector<std::string> words;
    // 初始容量(实现相关,通常0)
    std::cout << "初始容量: " << words.capacity() << "\n";  // 输出 0 
        
    words.reserve(3);  // 预分配3元素空间 
    std::cout << "reserve(3)后容量: " << words.capacity() << "\n";  // 输出 3 
        
    words = {"A", "B", "C", "D"};  // 可能触发扩容(容量可能变6)
    std::cout << "超容添加后容量: " << words.capacity() << "\n";  // 输出 4 
  • 极限容量检测(max_size()

    cpp 复制代码
    std::vector<double> data;
    std::cout << "系统支持最大元素数: " 
                << data.max_size() / 1e9 << " 亿"  // 示例输出 2.30584e+09 亿
                << "\n(实际受限于内存和系统架构)\n";
  • 内存回收操作(shrink_to_fit()

    cpp 复制代码
    std::vector<char> buffer(1000);
    buffer.erase(buffer.begin()+100, buffer.end());  // 删除900元素 
    
    std::cout << "删除后: size=" << buffer.size()        // 输出 100 
                << ", capacity=" << buffer.capacity()     // 输出 1000 
                << "\n";
    
    buffer.shrink_to_fit();  // 请求释放内存 
    std::cout << "shrink后: capacity=" << buffer.capacity()  // 输出 100(可能)
                << "\n(实际效果依赖编译器实现)\n";
  • size() vs capacity() 关系

  • 扩容策略优化(以GCC为例)

    cpp 复制代码
    // GCC扩容规则:new_cap = max(2*old_cap, required_size)
    std::vector<int> v;
    v.reserve(1);  // capacity=1 
    v.push_back(1);
    v.push_back(2);  // 触发扩容 → capacity=2 
    v.push_back(3);  // 再次扩容 → capacity=4 
  • shrink_to_fit() 的底层实现

    cpp 复制代码
    // 典型实现流程 
    if (size() < capacity()) {
        vector tmp(*this);        // 拷贝构造新vector(按size分配)
        swap(tmp);                // 交换数据指针 
    }

4. 操作示例

cpp 复制代码
// 向量的大小和容量
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
void print (vector<int>& vn) {
    std::cout << "大小:" << vn.size ()     << "   ";
    std::cout << "容量:" << vn.capacity () << std::endl;
    
    for (vector<int>::iterator it = vn.begin ();
        it != vn.end (); it++)
        cout << *it << ' ';
    
    cout << endl;
}

int main (void) {
    vector<int> v1 (10);
    print (v1);                 //大小:10   容量:10 //0 0 0 0 0 0 0 0 0 0
    
    v1.push_back (23);
    print (v1);                 //大小:11   容量:20 //0 0 0 0 0 0 0 0 0 0 23
    
    v1.resize (5);
    print (v1);                 //大小:5   容量:20 //0 0 0 0 0
    
    v1.resize (30);
    print (v1);                 //大小:30   容量:30 //0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
    
    for (int i = 0; i < 10; i++)//大小:20   容量:30
        v1.pop_back ();
    print (v1);                 //0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    
    v1.reserve (40);            //大小:20   容量:40
    print (v1);                 //0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    
    v1.reserve (10);            //大小:20   容量:40
    print (v1);                 //0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    
    v1.insert (v1.begin () + 4, 55);//大小:21   容量:40
    print (v1);                 //0 0 0 0 55 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    
    v1.erase (v1.begin () + 4);//大小:20   容量:40
    print (v1);                //0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    
    v1.insert (v1.begin () + 4, 66);//大小:21   容量:40
    print (v1);                //0 0 0 0 66 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    
    vector<int>::iterator it =
        find (v1.begin (), v1.end (), /*66*/88);
    if (it == v1.end ())
        cout << "没找到!" << endl;//没找到!
    else
        cout << *it << endl;   //66 
    
    int arr[] = {12, 56, 77, 45, 98, 100, 2};
    int *p = find (arr, arr + sizeof (arr) / sizeof (arr[0]), 45);
    cout << *p << endl;        // 45
    return 0;
}

五、修改

1. 函数说明

函数 说明 内存影响
clear() 清空所有元素 元素销毁,容量保留
push_back(const T& value) 尾部插入元素 可能触发扩容
pop_back() 删除尾部元素 尾部元素销毁(容量不变)
insert(iterator pos, const T& value) 指定位置插入 插入节点后元素后移
erase(iterator pos) 删除指定元素 删除节点后元素前移
resize(size_type count) 调整元素数量 增删默认构造元素
swap(vector& other) 交换两个容器内容 仅交换指针
assign() 完全替换 vector 内容 原元素全销毁

2. 关键操作解析

  • clear() vs resize(0)

    cpp 复制代码
    std::vector<int> v{1,2,3};
    v.clear();       // 内存状态: [  ] 容量=3
    v.resize(0);     // 效果相同(但依赖实现)
  • 插入删除的性能陷阱,头部插入和尾部插入都会使元素向后移动

    cpp 复制代码
    // 连续头部插入:O(n²) 性能灾难!
    for (int i = 0; i < 1000; ++i) 
        v.insert(v.begin(), i);  // 每次插入全体后移 
    
    // 优化方案:尾部插入后反转
    for (int i = 0; i < 1000; ++i) 
        v.push_back(i);
    std::reverse(v.begin(), v.end());
  • swap 的原子性优势

    cpp 复制代码
    current_data.swap(new_data); // 原子指针交换
  • assign 的隐蔽风险

    cpp 复制代码
    std::vector<std::string> names{"Alice", "Bob"};
    std::vector<std::string> temp = names; // 深拷贝备份 
    
    names.assign(1000, "Unknown"); // 原元素"Alice","Bob"被销毁 
    // 若异常抛出:原数据丢失且无恢复!

3. 操作示例

cpp 复制代码
#include <iostream>
#include <vector>

void print(const std::vector<int>& v, const char* msg) {
    std::cout << msg << ": [";
    for (int n : v) std::cout << n << " ";
    std::cout << "]  容量: " << v.capacity()  << "\n\n";
}

int main() {
    // === 1. 初始状态 === 
    std::vector<int> nums{10, 20, 30};
    print(nums, "初始状态");  // [10 20 30] 容量:3 

    // === 2. push_back:尾部插入 ===
    nums.push_back(40); 
    print(nums, "push_back(40)");  // [10 20 30 40] 容量:6(触发扩容)

    // === 3. pop_back:尾部删除 === 
    nums.pop_back(); 
    print(nums, "pop_back()");  // [10 20 30] 容量:6 

    // === 4. insert:指定位置插入 === 
    nums.insert(nums.begin()  + 1, 99);
    print(nums, "insert(pos1,99)");  // [10 99 20 30] 容量:6

    // === 5. erase:指定位置删除 ===
    nums.erase(nums.begin()  + 2);
    print(nums, "erase(pos2)");  // [10 99 30] 容量:6

    // === 6. resize:调整元素数量 === 
    nums.resize(5);  // 新增2个默认0 
    print(nums, "resize(5)");  // [10 99 30 0 0] 容量:6 

    nums.resize(2);  // 删除尾部3个
    print(nums, "resize(2)");  // [10 99] 容量:6 

    // === 7. assign:完全替换内容 ===
    nums.assign({7,  8, 9, 10});
    print(nums, "assign({7,8,9,10})");  // [7 8 9 10] 容量:6 

    // === 8. swap:容器交换 === 
    std::vector<int> other{100, 200};
    nums.swap(other); 
    print(nums, "swap后nums");  // [100 200] 容量:2 
    print(other, "swap后other"); // [7 8 9 10] 容量:6

    // === 9. clear:清空元素 === 
    nums.clear(); 
    print(nums, "clear()后");  // [] 容量:2(容量保留!)

    return 0;
}

4. 操作技巧

  • 容量预分配规则 ,提前把容量分配好,避免在循环里使用push_back()时扩容造成时间开销

    cpp 复制代码
    std::vector<int> data;
    data.reserve(1000);   // 预分配避免多次扩容
    for (int i=0; i<1000; ++i) 
        data.push_back(i); // 无扩容开销
  • 安全删除模式

    cpp 复制代码
    // 删除特定条件元素(避免迭代器失效)
    auto it = data.begin();
    while (it != data.end()) 
    {
        if (*it % 2 == 0) 
            it = data.erase(it); // erase返回下一有效迭代器 
        else 
            ++it;
    }
  • 移动语义优化

    cpp 复制代码
    // 在 words 尾部插入字符串变量 huge_str 使用移动语义而非拷贝
    std::vector<std::string> words;
    std::string huge_str = get_large_string();
    words.push_back(std::move(huge_str)); // 移动而非拷贝

六、迭代器操作

1. 函数说明

cpp 复制代码
begin() / end();       // 正向迭代器
rbegin() / rend();     // 反向迭代器
cbegin() / cend();     // 常量正向迭代器 
crbegin() / crend();   // 常量反向迭代器 

2. 关键操作解析

  • 随机访问:下标、迭代器

    cpp 复制代码
    vn[1] = 40;
    *(vn.begin () + 1) = 40;
  • 基础正向迭代(可读写)

    cpp 复制代码
    std::vector<int> nums{10, 20, 30};
    // 修改第二个元素 
    auto it = nums.begin() + 1;
    *it = 200;  // ✅ 允许修改 
    for (auto i = nums.begin(); i != nums.end(); ++i) 
        std::cout << *i << " ";  // 输出:10 200 30 
  • 反向迭代(镜像操作)

    cpp 复制代码
    std::vector<char> letters{'A','B','C'};
    // 逆序输出并修改首元素 
    auto rit = letters.rbegin();
    *rit = 'Z';  // ✅ 修改最后一个元素 
    for (auto r = letters.rbegin(); r != letters.rend(); ++r)
        std::cout << *r << " ";  // 输出:Z B A 
  • 常量反向迭代(安全遍历)

    cpp 复制代码
    const std::string str = "HELLO";
    // 逆序输出且无法修改 
    for (auto cr = str.crbegin(); cr != str.crend(); ++cr)
        std::cout << *cr;  // 输出:OLLEH 
  • 迭代器类型转换关系

    cpp 复制代码
    std::vector<int>::iterator          // begin()返回类型 
    std::vector<int>::const_iterator    // cbegin()返回类型 
    std::vector<int>::reverse_iterator  // rbegin()返回类型 
    std::vector<int>::const_reverse_iterator // crbegin()返回类型 
  • 与C风格数组的互操作

    cpp 复制代码
    int arr[] = {100, 200, 300};
    // 用标准库函数获取迭代器 
    auto arr_begin = std::begin(arr);  // ✅ 等效 &arr[0]
    auto arr_end = std::end(arr);      // ✅ 等效 &arr[3]

3. 操作示例

cpp 复制代码
#include <iostream>
#include <vector>

using namespace std;

void print (vector<int>& vn) 
{
    vector<int>::size_type size = vn.size ();
    cout << "元素个数:" << size << " -> ";
    
    for (vector<int>::size_type i = 0; i < size; i++)
        cout << vn[i] << ' ';
    
    cout << endl;
}
void show (vector<int>& vn) 
{
    for (vector<int>::iterator it = vn.begin (); it != vn.end (); it++)
        cout << *it << ' ';
    cout << endl;
}

int main (void) 
{
    vector<int> vn;
    vn.push_back (10);
    vn.push_back (20);
    vn.push_back (30);
    
    print (vn);        // 元素个数:3 -> 10 20 30
    show (vn);        // 10 20 30
    
    vn.pop_back ();
    print (vn);        // 元素个数:2 -> 10 20
    
    vn[1] = 40;
    print (vn);        // 元素个数:2 -> 10 40
    
    vector<int>::const_iterator it = vn.begin ();
    it++;
    //    (*it)++; // it的目标不可修改,如同常量指针
    // const xxx *或xxx const *
    // 而const vector<int>::iterator it 表示迭代器本身不可修改,如同指针常量
    // xxx * const
    vector<int> v1 (10);
    print (v1);        // 元素个数:10 -> 0 0 0 0 0 0 0 0 0 0
    
    vector<int> v2 (10, 5);
    print (v2);        // 元素个数:10 -> 5 5 5 5 5 5 5 5 5 5
    
    v1[0] = 100;
    v1[9] = 200;
    cout << v1.front () << ' ' << v1.back() << endl;//100 200
    return 0;
}

4. 注意事项

  • 类型推导陷阱

    cpp 复制代码
    auto it1 = nums.begin();   // 推导为iterator(可修改)
    auto it2 = nums.cbegin();  // 推导为const_iterator(安全)
  • 范围循环本质

    cpp 复制代码
    for (int n : nums) { /*...*/ } 
    // 等价于:
    for (auto it = nums.begin(); it != nums.end(); ++it)
  • 迭代器失效场景

    cpp 复制代码
    std::vector<int> data{1,2,3};
    auto it = data.begin();
    data.push_back(4);  // 可能触发扩容 
    *it = 5;  // ❌ 迭代器已失效(未定义行为)

七、高效操作(C++11+)

1. 函数说明

函数 说明
emplace_back(Args&&... args) 直接构造尾部元素
emplace(iterator pos, Args&&... args) 直接构造指定位置元素

2. 关键操作解析

cpp 复制代码
// 直接构造对象(避免拷贝)
struct Point { int x, y; };
std::vector<Point> points;
points.emplace_back(3,  4);  // 直接调用构造函数 
cpp 复制代码
// ✅ 推荐使用emplace的情况 
v.emplace_back("text",  100);  // 构造参数简单 
v.emplace(v.begin(),  1, 2, 3); // 插入位置敏感 

// ⚠️ 不推荐使用的情况 
v.emplace_back(existing_obj);   // 已有对象直接push_back更清晰 
cpp 复制代码
// 简单类型(int/double等)
v.push_back(42);           // ✅ 清晰简洁 

// 多参数构造的复杂类型 
v.emplace_back("ID007",  9.8, Vector3D());  // ✅ 避免临时对象 

// 在迭代器中间插入 
v.emplace(v.begin()  + 2, config);  // ✅ 减少元素移动次数 

// 对象已存在时 
auto obj = HeavyObject(...);
v.push_back(std::move(obj));   // ✅ 明确所有权转移 

3. 常见错误规避

错误案例 后果 修正方案
emplace_back({1,2,3}) 编译错误(初始化列表歧义) emplace_back(std::initializer_list{1,2,3})
emplace(pos, arg) 后使用旧迭代器 迭代器失效 接收返回的新迭代器 it = emplace(...)
忽略移动语义类型 意外拷贝操作 对移动类型显式使用 std::move

4. 优先选择原则

cpp 复制代码
// 简单类型(int/double等)
v.push_back(42);          // ✅ 清晰简洁 

// 多参数构造的复杂类型 
v.emplace_back("ID007", 9.8, Vector3D());  // ✅ 避免临时对象 

// 在迭代器中间插入 
v.emplace(v.begin() + 2, config);  // ✅ 减少元素移动次数 

// 对象已存在时 
auto obj = HeavyObject(...);
v.push_back(std::move(obj));  // ✅ 明确所有权转移 

八、非成员函数

1. 函数说明

函数 说明
operator==, !=, <, > 容器比较
std::swap(vector<T>& lhs, rhs) 特化交换
erase(vector<T>& c, const T& value) 删除匹配值(C++20)
erase_if(vector<T>& c, Predicate pred) 条件删除(C++20)

2. 示例代码

  • 容器比较运算符

    cpp 复制代码
    std::vector a{1,2,3}, b{1,2,3}, c{9};
    
    // 等值比较(需元素数量和值相同)
    bool eq = (a == b);  // ✅ true(深层次比较)
    
    // 字典序比较(常用于排序)
    bool lt = (c < a);   // ✅ true(9<1)
  • 特化交换函数

    cpp 复制代码
    std::vector v1{10,20}, v2{30};
    
    // 高性能交换(无拷贝,仅交换指针)
    std::swap(v1, v2);  
    
    // v1: [30]|v2: [10,20](容量同步交换)
  • 值匹配删除 erase() (C++20)

    cpp 复制代码
    std::vector nums{5,2,5,8};
    
    // 删除所有值为5的元素 
    std::erase(nums, 5);  
    
    // 结果:[2,8](返回删除数量)
  • 条件删除 erase_if() (C++20)

    cpp 复制代码
    std::vector data{-3,7,-1,0};
    
    // 删除所有负数(λ表达式)
    std::erase_if(data, [](int x){return x<0;});  
    
    // 结果:[7,0](支持复杂逻辑)

3. 操作对比总结

函数 典型场景 优势
operator== 配置比对/结果验证 深度比较容器内容
std::swap(vector&, vector&) 线程安全数据交换 零拷贝原子操作
erase(vector&, value) 批量删除特定值 比循环+erase高效
erase_if(vector&, pred) 复杂条件删除(如负/奇数) 避免手写易出错的迭代器逻辑

九、完整应用示例

1. 类对象的向量

  • 如果一个类类型的对象需要被存储在向量中,那么这个类至少应该支持无参构造,以确保为这个向量所分配的内存能够被正确的初始化。
  • 该类可能还需要支持拷贝构造函数和拷贝赋值操作符重载函数。
  • 如有必要可能还需要支持"=="和"<"操作符。
cpp 复制代码
// 类对象的向量
#include <iostream>
#include <vector>

using namespace std;

class Integer {
public:
    Integer (void) : m_data (0) {
        cout << "无参构造:" << this << endl;
    }
    Integer (int data) : m_data (data) {
        cout << "有参构造:" << this << endl;
    }
    ~Integer (void) {
        cout << "析构函数:" << this << endl;
    }
    Integer (const Integer& that) :
        m_data (that.m_data) {
        cout << "拷贝构造:" << &that << " -> " <<
            this << endl;
    }
    Integer& operator= (const Integer& that) {
        cout << "拷贝赋值:" << &that << " -> " <<
            this << endl;
        if (&that != this)
            m_data = that.m_data;
        return *this;
    }
    
    bool operator== (const Integer& that) const {
        return m_data == that.m_data;
    }
    bool operator< (const Integer& that) const {
        return m_data > that.m_data;
    }
    
    int m_data;
};

int main (void) {
    cout << "-------- 1 --------" << endl;
    vector<Integer> vi (5, Integer (100));//1次有参构造 5次拷贝构造   6次析构
    cout << "-------- 2 --------" << endl;
    vi.erase (vi.begin ());               //删除节点后元素前移 //4次拷贝赋值 5次析构  
    cout << "-------- 3 --------" << endl;
    vi.insert (vi.begin (), Integer (200));//插入节点后元素后移 //1次有参构造 1次拷贝构造 4次拷贝赋值 6次析构  
    cout << "-------- 4 --------" << endl;
    vi.resize (1);                         //调整容器大小为1   //5 次析构  
    cout << "-------- 5 --------" << endl;

    vi.resize (5);                         //调整容器大小为5   //5 次析构  
    cout << vi.capacity() << endl;         //5
    cout << "-------- 6 --------" << endl;
    vi.reserve (10);                       //调整容器大小为 10  //5次拷贝构造 添加5个初始化的0元素
    cout << "-------- X --------" << endl;
    vi[0] = 12;                            //分别调用5次 有参构造 拷贝赋值 析构函数:
    vi[1] = 23;
    vi[2] = 47;
    vi[3] = 33;
    vi[4] = 47;
    
    return 0;
}

2. 通过数组初始化向量

  • 核心代码:vector<int> v1(&arr[0], &arr[5]);

    cpp 复制代码
    int arr[5] = {1, 2, 3, 4, 5};
    vector<int> v1 (&arr[0], &arr[5]);
  • 迭代器范围原理:

    • &arr[0] 指向首元素(包含)

    • &arr[5] 是尾后指针(不包含)

    • 实际构造范围:[arr[0], arr[5]) → 等价于 arr[0]到arr[4]

      cpp 复制代码
      // 用容器初始化容器
      #include <iostream>
      #include <vector>
      using namespace std;
      int main (void) {
          int arr[5] = {1, 2, 3, 4, 5};
          vector<int> v1 (&arr[0], &arr[5]);//实际构造范围:[arr[0], arr[5]) → 等价于 arr[0]到arr[4]
          
          for (int i = 0; i < v1.size (); i++)
              cout << v1[i] << ' ';
          cout << endl;               // 1 2 3 4 5
          
          vector<int> v2 (v1.begin () + 1, v1.end () - 1);
          for (int i = 0; i < v2.size (); i++)
              cout << v2[i] << ' ';
          cout << endl;               // 2 3 4
          
          for (vector<int>::reverse_iterator it = v1.rbegin (); it != v1.rend (); it++)
              cout << *it << ' ';         // 5 4 3 2 1
          cout << endl;
          return 0;
      }

十、关键特性

  1. 动态内存管理:自动扩容(通常按2倍扩容)
  2. 连续存储:支持指针算术操作
  3. 异常安全:强异常保证(部分操作)
  4. 时间复杂度:
    • 随机访问:O(1)
    • 尾部插入/删除:均摊O(1)
    • 中间插入/删除:O(n)

十一、应用注意事项⚖️

1. 完美转发陷阱

cpp 复制代码
std::vector<std::unique_ptr<Device>> devices;
// 错误:尝试复制 unique_ptr 
// devices.emplace_back(new Device()); 

// 正确:显式移动 
devices.emplace_back(std::make_unique<Device>());

2. 高性能尾部操作

cpp 复制代码
vector<int> data;
data.reserve(1000);  // 预分配避免多次扩容
for(int i=0; i<1000; ++i) 
data.emplace_back(i);

// C++20 安全删除
erase_if(data, [](int x){ return x%2==0; });

3. 内存释放替代方案

cpp 复制代码
// 强制释放内存的可靠方法 
std::vector<int> huge_data = get_data();
std::vector<int>(huge_data).swap(huge_data);  // 通过临时对象交换 

4. 函数选择指南

场景 推荐操作 理论依据
容器状态检测 empty()替代size()==0 语义更清晰
高性能批量插入 reserve()预分配 避免多次扩容开销
内存敏感型系统 shrink_to_fit() 减少内存占用(可能有效)
跨平台开发 避免依赖max_size() 不同系统差异过大
实时系统 慎用动态扩容 防止不可预测的内存分配延迟

5. 避免裸指针操作

cpp 复制代码
// 改用data()获取首元素指针 
vector<int> v{1,2,3};
int* p = v.data();   // 等效&v[0]但更安全 

实际开发中,优先使用 emplace_back() 替代 push_back() 避免拷贝,结合 reserve() 可显著提升性能。

相关推荐
努力努力再努力wz12 分钟前
【c++深入系列】:万字详解模版(下)
java·c++·redis
林开落L30 分钟前
库的制作与原理
linux·开发语言·动静态库·库的制作
m0_480502641 小时前
Rust 入门 泛型和特征-特征对象 (十四)
开发语言·后端·rust
瓦特what?1 小时前
关于C++的#include的超超超详细讲解
java·开发语言·数据结构·c++·算法·信息可视化·数据挖掘
祁同伟.2 小时前
【C++】动态内存管理
开发语言·c++
励志不掉头发的内向程序员2 小时前
C++基础——内存管理
开发语言·c++
lifallen2 小时前
JCTools 无锁并发队列基础:ConcurrentCircularArrayQueue
java·开发语言·数据结构·算法
jdlxx_dongfangxing2 小时前
从希格斯玻色子到 QPU:C++ 的跨维度征服
c++·量子计算·希格斯玻色子·高难度科普文
千里镜宵烛3 小时前
深入理解 Linux 线程:从概念到虚拟地址空间的全面解析
开发语言·c++·操作系统·线程