C++小白记:vector

目录

1.引言

什么是向量?

向量与数组的比较:

[2. std::vector 基础](#2. std::vector 基础)

[2.1 包含头文件](#2.1 包含头文件)

[2.2 定义与初始化](#2.2 定义与初始化)

[2.3 向量的大小与容量](#2.3 向量的大小与容量)

[3. 向量的基本操作](#3. 向量的基本操作)

[3.1 添加与删除元素](#3.1 添加与删除元素)

[3.2 访问元素](#3.2 访问元素)

[3.3 遍历向量](#3.3 遍历向量)

[3.4 修改元素](#3.4 修改元素)

[4. 向量的高级用法](#4. 向量的高级用法)

[4.1 嵌套向量(二维向量)](#4.1 嵌套向量(二维向量))

[4.2 向量与其他数据结构结合](#4.2 向量与其他数据结构结合)

[4.3 使用迭代器操作向量](#4.3 使用迭代器操作向量)

[5. 常用算法与向量](#5. 常用算法与向量)

[5.1 排序](#5.1 排序)

[5.2 反转](#5.2 反转)

[5.3 查找](#5.3 查找)

[6. 向量的性能与优化](#6. 向量的性能与优化)

[6.1 内存管理](#6.1 内存管理)

[6.2 预留空间](#6.2 预留空间)

[6.3 收缩容量](#6.3 收缩容量)

结语:


1.引言

什么是向量?

向量Vector)是 C++ 标准模板库(STL)中的一种序列容器,能够动态地管理可变大小的数组。与传统的固定大小的数组不同,向量可以根据需要随时调整其大小,提供更高的灵活性和便利性。

向量与数组的比较:

特性 数组(Array) 向量(Vector)
大小 固定大小(编译时或运行时) 动态可变大小
内存管理 手动管理(需要预留足够空间) 自动管理(自动扩展或收缩)
支持的操作 限制较多 丰富的成员函数和操作
安全性 较低(易发生缓冲区溢出) 较高(通过成员函数进行边界检查)
与 STL 算法的兼容性

2. std::vector 基础

2.1 包含头文件

使用 std::vector 需要包含 <vector> 头文件:

#include <vector>

2.2 定义与初始化

定义一个整数向量:

cpp 复制代码
std::vector<int> numbers;

定义一个字符串向量:

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

std::vector<std::string> words;

初始化向量:

默认初始化:

cpp 复制代码
std::vector<int> vec1; // 空向量

指定大小和默认值:

cpp 复制代码
std::vector<int> vec2(5, 10); // 5个元素,值均为10

使用初始化列表:

cpp 复制代码
std::vector<int> vec3 = {1, 2, 3, 4, 5};

采用移动构造:

cpp 复制代码
std::vector<int> vec5(std::move(vec4)); // 移动vec4到vec5

注意 :移动构造时接着访问vec4,编译会崩溃,或者输出很乱的数字,相当于vec4作废了

示例代码:

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

int main() {
    // 默认初始化
    std::vector<int> vec1;
    
    // 指定大小和默认值
    std::vector<int> vec2(5, 10);
    
    // 使用初始化列表
    std::vector<int> vec3 = {1, 2, 3, 4, 5};
    
    // 拷贝构造
    std::vector<int> vec4(vec3);
    
    // 移动构造
    std::vector<int> vec5(std::move(vec4));
    
    // 输出vec2
    std::cout << "vec2: ";
    for(auto num : vec2) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    // 输出vec3
    std::cout << "vec3: ";
    for(auto num : vec3) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    // 输出vec5
    std::cout << "vec5: ";
    for(auto num : vec5) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

输出:

2.3 向量的大小与容量

size():返回向量中元素的数量。

capacity():返回向量目前为止分配的存储容量。

empty():检查向量是否为空。

注意: empty为空,是说size=0,但是capacity不见得就等于0。

示例代码:

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

int main() {
    std::vector<int> vec = {1, 2, 3};
    
    std::cout << "Size: " << vec.size() << std::endl;       // 输出: 3
    std::cout << "Capacity: " << vec.capacity() << std::endl; // 输出: 3(或更大,取决于实现)
    
    std::cout << "Is empty? " << (vec.empty() ? "Yes" : "No") << std::endl; // 输出: No
    
    vec.reserve(10); // 预留容量
    std::cout << "After reserve(10), Capacity: " << vec.capacity() << std::endl; // 输出: 10
    
    vec.shrink_to_fit(); // 收缩到适合大小
    std::cout << "After shrink_to_fit(), Capacity: " << vec.capacity() << std::endl; // 输出: 3
    
    return 0;
}

输出:

注意: capacity() 并不一定精确匹配 size(),它表示在需要重新分配内存之前,向量可以容纳的元素数量。


3. 向量的基本操作

3.1 添加与删除元素

push_back():在向量末尾添加一个元素。

pop_back():移除向量末尾的元素。

insert():在指定位置插入元素。

erase():移除指定位置的元素或范围内的元素。

clear():移除所有元素。

示例代码:

cpp 复制代码
 std::vector<int> vec;
    vec.push_back(10);
    vec.push_back(20);
    vec.push_back(30);
     std::cout << "执行push_back添加元素后:" << std::endl;
   for (int temp: vec) {
        std::cout << temp << " ";
    }

    std::cout << std::endl;
    // 删除容器最后一个元素
    vec.pop_back();

    std::cout << "执行pop_back删除尾部元素后:" << std::endl;
    for (int temp: vec) {
        std::cout << temp << " ";
    }

    std::cout << std::endl;

    // 在第二个位置插入数值25(begin()+1代表第二个元素迭代器)
    vec.insert(vec.begin() + 1, 25);
    std::cout << "执行insert插入元素后:" << std::endl;
    for (int temp: vec) {
        std::cout << temp << " ";
    }

    std::cout << std::endl;
    // 删除第二个位置的元素
    vec.erase(vec.begin() + 1);

    std::cout << "执行erase删除指定位置元素后:" << std::endl;
    for (int temp: vec) {
        std::cout << temp << " ";
    }

    std::cout << std::endl;
    // 清空容器所有元素,size变为0,但容量不变
    vec.clear();
    std::cout << "clear清空后,size: " << vec.size() << std::endl; // 输出: 0
    std::cout << "clear清空后,capacity: " << vec.capacity() << std::endl; //不变

输出:

复制代码
After push_back: 10 20 30 
After pop_back: 10 20 
After insert: 10 25 20 
After erase: 10 20 
After clear, size: 0

这里牵扯到了一个内存溢出 的情况,对于这个问题我们可以采取立即回收机制,用 swap来解决,后面还会介绍一个方法:

cpp 复制代码
// 立即回收机制,释放多余内存的标准写法:空容器swap收缩内存
    {
        std::vector<int> empty_vec;
        empty_vec.swap(vec);  //vec的容量给了enpty_vec,在这个作用域内会被回收

        std::cout << "swap交换后,vec容量: " << vec.capacity() << std::endl;
        std::cout << "swap交换后,临时空容器容量: " << empty_vec.capacity() << std::endl;
    }

输出:

因为 empty_vec 是临时变量在这个作用域里,花括号结束就会自动被回收清除。

3.2 访问元素

operator[]:通过索引访问元素。

at():通过索引访问元素,带边界检查。

front():访问第一个元素。

back():访问最后一个元素。

示例代码:

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

int main() {
    std::vector<std::string> fruits = {"Apple", "Banana", "Cherry"};
    
    // 使用operator[]访问元素,下标[]访问第一个元素
    std::cout << "First fruit: " << fruits[0] << std::endl; // 输出: Apple
    
    // at()访问第二个元素,越界会抛异常
    try {
        std::cout << "Second fruit: " << fruits.at(1) << std::endl; // 输出: Banana
        std::cout << "Invalid fruit: " << fruits.at(5) << std::endl; // 抛出异常
    }
    catch(const std::out_of_range& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    
    // 使用front()和back()
    std::cout << "Front: " << fruits.front() << std::endl; // 输出: Apple
    std::cout << "Back: " << fruits.back() << std::endl;   // 输出: Cherry
    
    return 0;
}

输出:

复制代码
First fruit: Apple
Second fruit: Banana
Exception: vector::_M_range_check: __n (which is 5) >= this->size() (which is 3)
Front: Apple
Back: Cherry

3.3 遍历向量

1.使用范围 for 循环

2.使用传统 for 循环

3.使用迭代器

示例代码:

cpp 复制代码
std::vector<std::string> fruits={"apple", "banana", "orange"};
// C++11范围for循环遍历容器
     for (auto temp: fruits) {
        std::cout << temp << " ";
    }

    std::cout << std::endl;
    // 传统下标循环遍历
    for (size_t i = 0; i < fruits.size();i++) {
        std::cout << "fruit: " << fruits[i] << " ";
    }
    std::cout << std::endl;
  // 迭代器遍历容器,迭代器本质就是指针
    for (auto it = fruits.begin(); it != fruits.end();it++) {
        std::cout << "fruit: " << *it << std::endl;  //*it解引用的方式取出数据
    }

输出:

3.4 修改元素

1.通过索引或迭代器修改

2.使用 assign() 重新赋值

3.替换整个向量内容

示例代码:

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

int main() {
    std::vector<int> vec = {10, 20, 30, 40, 50};
    
    // 通过索引修改元素
    vec[2] = 35;
    
    // 使用 at() 修改元素
    vec.at(4) = 55;
    
    // 使用迭代器修改元素
    for(auto it = vec.begin(); it != vec.end(); it++) {
        if(*it == 20) {  // 尾部迭代器前移指向最后一个元素,解引用修改
            *it = 25;
        }
    }
    
    // 输出修改后的向量
    std::cout << "Modified vector: ";
    for(auto num : vec) {
        std::cout << num << " ";
    }
    std::cout << std::endl; // 输出: 10 25 35 40 55 
    
    return 0;
}

输出:

复制代码
Modified vector: 10 25 35 40 55 

4. 向量的高级用法

4.1 嵌套向量(二维向量)

向量可以包含其他向量,形成多维数组结构。

示例代码:二维向量

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

int main() {
    // 定义一个3x4的二维向量,初始化为0
    std::vector<std::vector<int>> matrix(3, std::vector<int>(4, 0));
    
    // 填充矩阵
    for(int i = 0; i < 3; i++) {
        for(int j = 0; j < 4; j++) {
            matrix[i][j] = i * 4 + j + 1;
        }
    }
    
    // 输出矩阵
    std::cout << "Matrix:" << std::endl;
    for(auto row : matrix) {
        for(auto elem : row) {
            std::cout << elem << "\t";
        }
        std::cout << std::endl;
    }
    
    return 0;
}

输出:

复制代码
Matrix:
1	2	3	4	
5	6	7	8	
9	10	11	12	

4.2 向量与其他数据结构结合

向量可以与结构体、类等其他数据结构结合使用,增强数据组织能力。

示例代码:向量与结构体结合

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

// 定义学生结构体
struct Student {
    int id;
    std::string name;
    float grade;
};

int main() {
    // 定义一个学生向量,空的默认构造
    std::vector<Student> students;
    
   // 列表初始化插入学生对象
    students.push_back({1001, "Alice", 89.5});
    students.push_back({1002, "Bob", 92.0});
    students.push_back({1003, "Charlie", 85.0});
    
   // 常量引用类型遍历学生,可以在循环里修改,避免拷贝
    for(const auto& student : students) {
        std::cout << "ID: " << student.id 
                  << ", Name: " << student.name 
                  << ", Grade: " << student.grade << std::endl;
    }
    
    return 0;
}

输出:

复制代码
ID: 1001, Name: Alice, Grade: 89.5
ID: 1002, Name: Bob, Grade: 92
ID: 1003, Name: Charlie, Grade: 85

4.3 使用迭代器操作向量

迭代器是一种指针类型,用于遍历和操作容器中的元素。

示例代码:使用迭代器

cpp 复制代码
// 重新赋值容器
    vec = {10, 20, 30, 40, 50};

    // 使用迭代器遍历并修改每个元素
    for(auto it = vec.begin(); it != vec.end(); it++) {
        *it += 5;
    }

    // 输出修改后的容器
    std::cout << "所有元素+5后: ";
    for(auto it = vec.begin(); it != vec.end(); it++) {
        std::cout << *it << " ";
    }
    std::cout << std::endl; // 输出: 15 25 35 45 55

5. 常用算法与向量

5.1 排序

可以使用 <algorithm> 头文件中的 sort() 函数对向量进行排序。

示例代码:对整数向量排序

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

int main() {
    std::vector<int> numbers = {50, 20, 40, 10, 30};
    
    // 排序前
    std::cout << "Before sorting: ";
    for(auto num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    //  sort默认升序排序
    std::sort(numbers.begin(), numbers.end());
    
    // 排序后
    std::cout << "After sorting: ";
    for(auto num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

输出:

复制代码
Before sorting: 50 20 40 10 30 
After sorting: 10 20 30 40 50 

降序:

cpp 复制代码
std::vector<int > numbers = {6,1,0,5,2,8}; 
// 降序排序,传入greater比较器
    std::sort(numbers.begin(), numbers.end(),std::greater<int>());  
//greater为仿函数

    // 降序排序后输出
    std::cout << "降序排序后: ";
    for(auto num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

5.2 反转

使用 reverse() 函数可以反转向量中的元素顺序。

示例代码:

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

int main() {
    std::vector<char> letters = {'A', 'B', 'C', 'D', 'E'};
    
    std::cout << "Before reversing: ";
    for(auto c : letters) {
        std::cout << c << " ";
    }
    std::cout << std::endl;
    
    // 反转向量
    std::reverse(letters.begin(), letters.end());
    
    std::cout << "After reversing: ";
    for(auto c : letters) {
        std::cout << c << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

输出:

复制代码
Before reversing: A B C D E 
After reversing: E D C B A 

5.3 查找

使用 find() 函数可以在向量中查找特定元素。

示例代码:

cpp 复制代码
//查找
    fruits = {"Apple", "Banana", "Cherry", "Lemon"};
    std::string target = "Watermelon";

    // find查找指定元素,返回迭代器
    auto it=find(fruits.begin(),fruits.end(),target);
    if(it != fruits.end()) {
        std::cout << "找到元素: " << *it << std::endl;  //解引用取数据
    }else{
        std::cout << "未找到目标: " << target << std::endl;
    }

6. 向量的性能与优化

6.1 内存管理

向量会动态地管理内存,自动调整其容量以适应新增或删除的元素。频繁的内存分配可能会影响性能。

6.2 预留空间

这就是上面解决内存问题的第二个方法,使用 reserve() 可以提前为向量分配足够的内存,减少内存重新分配的次数,提高性能。

示例代码:

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

int main() {
    std::vector<int> vec;
    
    //用reserve预分配内存空间
    vec.reserve(1000);
    std::cout << "Capacity after reserve(1000): " << vec.capacity() << std::endl;
    
    // 添加元素
    for(int i = 0; i < 1000; ++i) {
        vec.push_back(i);
    }
    
    std::cout << "Size after adding elements: " << vec.size() << std::endl;
    std::cout << "Capacity after adding elements: " << vec.capacity() << std::endl;
    
    return 0;
}

输出:

复制代码
Capacity after reserve(1000): 1000
Size after adding elements: 1000
Capacity after adding elements: 1000

6.3 收缩容量

使用 shrink_to_fit() 可以请求收缩向量的容量以匹配其大小,释放多余的内存。

示例代码:

cpp 复制代码
//shrink_to_fit()收缩内存
    {
        std::vector<int> vec;

        // 预分配较大内存
        vec.reserve(1000);
        std::cout << "添加元素前容量: " << vec.capacity() << std::endl;

        // 仅添加少量元素
        vec.push_back(1);
        vec.push_back(2);
        vec.push_back(3);
        std::cout << "添加3个元素后size: " << vec.size() << std::endl;
        std::cout << "添加3个元素后容量: " << vec.capacity() << std::endl;

        // 收缩内存,容量匹配当前元素数量
        vec.shrink_to_fit();
        std::cout << "shrink_to_fit收缩内存后容量: " << vec.capacity() << std::endl;
    }

输出示例:


结语:

今日任务就是学习vector变量相关用法,Fighting!

相关推荐
tkevinjd19 小时前
力扣72-编辑距离
算法·leetcode·职场和发展
小刘学技术19 小时前
AI人工智能决策树分类器:原理、实现与应用
开发语言·人工智能·python·算法·决策树·机器学习·数据挖掘
脱胎换骨-军哥19 小时前
C++ 嵌入式编程实例:从寄存器操作到底层驱动开发
开发语言·c++·驱动开发
鱼子星_20 小时前
【C++】vector
开发语言·c++·笔记·stl
double_eggm20 小时前
uniapp.3
开发语言·前端·javascript
白玉cfc20 小时前
熟悉Objective-C
开发语言·ios·objective-c
吃着火锅x唱着歌20 小时前
Effective C++ 学习笔记 条款38 通过复合塑模出has-a或“根据某物实现出”
c++·笔记·学习
syagain_zsx20 小时前
库制作与原理 · 链接知识笔记
c语言·c++·笔记·动态库·静态库
qz5zwangzihan120 小时前
题解:Atcoder Beginner Contest abc467 A~D
c++·题解·atcoder·abc467