【C++】 vector基础用法

C++ vector 基本操作

1. 引入 vector

vector 是C++标准库中的一种容器,用于实现动态数组,能够自动调整其大小。使用前需要包含 <vector> 头文件。

#include <vector>
using namespace std;

2. 初始化与构造

构造方法描述
vector<T>()创建一个空的 vector
vector<T>(size_type n)创建含有 n 个元素的 vector,元素默认初始化
vector<T>(size_type n, const T& value)创建含有 n 个元素的 vector,元素初始化为 value
vector<T>(const initializer_list<T>& il)从一个 initializer_list 构造 vector
vector<T>(InputIt first, InputIt last)从迭代器区间 [first, last) 构造 vector

示例:

vector<int> v1;            // 空 vector
vector<int> v2(5);         // 5个默认初始化的 int 元素
vector<int> v3(5, 42);     // 5个元素,每个元素都是 42
vector<int> v4{1, 2, 3};   // 从初始化列表构造
vector<int> v5(v4.begin(), v4.end()); // 从迭代器区间构造

3. 访问元素

方法描述
operator[]快速访问元素,不安全
at()安全访问元素,越界时抛出异常
front()获取第一个元素
back()获取最后一个元素

示例:

int first = v4.front(); // 第一个元素
int last = v4.back();   // 最后一个元素
int second = v4.at(1);  // 安全访问第二个元素

4. 容量与大小

方法描述
size()返回当前元素数量
empty()判断 vector 是否为空
capacity()返回当前容量
reserve(size_type n)请求预留足够空间
shrink_to_fit()尝试减小容量

流程图示例:

5. 修改操作

方法描述
push_back(value)在末尾添加一个元素
pop_back()移除最后一个元素
clear()清空所有元素
insert(pos, value)pos 插入元素
erase(pos)删除 pos 位置的元素
resize(size, value)改变元素数量,可指定填充值

示例:

v4.push_back(99); // 在末尾添加一个元素
v4.pop_back();    // 移除最后一个元素
v4.clear();       // 清空所有元素
v4.insert(v4.begin(), 0); // 在开始处插入一个元素
v4.erase(v4.begin()+1);   // 删除第二个元素
v4.resize(10, -1);        // 调整大小为10,不足部分填充-1

6. 迭代器

方法描述
begin()返回指向第一个元素的迭代器
end()返回指向最后一个元素之后位置的迭代器
rbegin()返回指向最后一个元素的逆向迭代器
rend()返回指向第一个元素之前位置的逆向迭代器

示例:

for (auto it = v4.begin(); it != v4.end(); ++it) {
    cout << *it << ' ';
}
cout << endl;

7. 内存管理与性能考虑

vector 在内部使用连续内存块存储数据,这使得随机访问非常高效。当 vector 的容量不足以容纳新元素时,它会重新分配更大的内存块并复制现有元素,通常新容量是原容量的两倍,这一过程可能带来较大的性能开销。

  • 扩容策略:当 vector 需要更多空间时,它通常会申请比当前容量大得多的空间。这种策略有助于减少频繁的重新分配,但可能会导致内存使用效率降低。

  • 性能影响:由于 vector 的这种内部实现,插入和删除操作在非尾部位置执行时,会导致大量元素的移动,从而产生较高的时间复杂度。因此,在已知元素数量的情况下,预先调用 reserve 可以显著提高性能。

8. 实例演示:动态数组的使用

#include <iostream>
#include <vector>
using namespace std;
int main() {
    vector<int> numbers; // 创建空 vector

    // 向 vector 添加元素
    for (int i = 0; i < 10; ++i) {
        numbers.push_back(i);
    }

    // 使用迭代器遍历 vector
    for (auto it = numbers.begin(); it != numbers.end(); ++it) {
        cout << *it << " ";
    }
    //--> 0 1 2 3 4 5 6 7 8 9
    cout << endl;

    // 使用 range-based for 循环遍历 vector
    for (const auto& num : numbers) {
        cout << num << " ";
    }
    //--> 0 1 2 3 4 5 6 7 8 9
    cout << endl;

    // 使用 at() 方法访问元素
    try {
        cout << numbers.at(5) << endl;
    }
    catch (out_of_range& e) {
        cerr << "Error: " << e.what() << endl;
    }
    //--> 5

    return 0;
}

9. 性能优化技巧

  • 预分配内存:使用 reserve 函数提前分配足够的内存,避免多次重新分配。
  • 避免不必要的拷贝:尽可能使用 emplaceemplace_back 来在容器中直接构造对象,这样可以避免不必要的拷贝或移动。
  • 使用迭代器:在遍历或修改容器时,优先使用迭代器,这样可以避免数组下标访问的边界检查开销。
  • 适时使用 shrink_to_fit:在不再需要额外内存时,调用此函数释放未使用的内存,但这不应过度使用,因为它可能导致性能下降。

10. 常见问题解答

Q: 如何在 vector 中查找特定元素?
  • A: 可以使用算法如 find 或者自定义循环来查找元素。
#include <iostream>
#include <vector>
#include <algorithm>     // 包含 find
using namespace std;
int main() {
    vector<int> numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int target = 5;      // 指定要查找的元素
    // 使用 find 查找目标元素的位置
    auto rit = find(numbers.begin(), numbers.end(), target);
    if (rit != numbers.end())
        // 如果找到元素,打印其位置(基于0的索引)
        cout << "找到元素的位置: " << distance(numbers.begin(), rit) << endl;
        //--> 找到元素的位置:4
    else
        // 如果没有找到元素
        cout << "在向量中未找到元素。" << endl;
    return 0;
}
Q: 如何在 vector 中插入元素而不改变其迭代器的有效性?
  • A: 如果你希望插入元素后,原有的迭代器仍然有效,可以尝试以下两种方法之一:

    1. 在向量的末尾插入元素。使用 push_backemplace_back,它们不会使先前存在的迭代器失效。
    2. 在向量中某个已知位置之后插入元素。例如,如果你知道元素将被插入在向量的末尾或某个固定位置之后,那么插入操作不会使之前的迭代器失效。
    vector<int> vec = {1, 2, 3};
    auto it = vec.begin();
    
    // 在末尾插入元素,之前的迭代器仍然有效
    vec.push_back(4);
    
    // 在已知位置之后插入元素,之前的迭代器仍然有效
    vec.insert(it + 2, 99);
    
Q: vector 中的 reserveresize 有何区别?
  • A: reserveresize 都用于改变 vector 的大小,但它们的工作方式和用途有所不同:

    • reserve:仅请求额外的内存空间,但不会改变向量的大小或其元素数量。它的主要目的是为了避免频繁的重新分配,提高性能。
    • resize:改变向量的大小,如果新大小大于当前大小,将用指定的值填充剩余的空间;如果新大小小于当前大小,则会截断多余的元素。
    vector<int> vec;
    
    // 预留足够的内存空间存储100个元素,但实际元素数量仍为0
    vec.reserve(100);
    
    // 将向量大小调整为10,不足的部分用0填充
    vec.resize(10, 0);
    
Q: 迭代器失效问题
错误示例(插入与删除)

考虑一个std::vector,直接在遍历中进行插入或删除操作将导致迭代器失效:

vector<int> vec = {1, 2, 3, 4};
for (auto it = vec.begin(); it != vec.end(); ++it) {
    if (*it == 2) {
        vec.insert(it, 99); // 错误:插入操作使迭代器失效
    }
    if (*it == 3) {
        vec.erase(it); // 错误:删除操作使迭代器失效
    }
}

问题分析:插入或删除元素会导致容器内部结构发生变化,使得迭代器指向的位置不再有效。

正确用法(更新迭代器)

应当使用inserterase函数返回的迭代器来更新当前迭代器,以避免失效问题:

vector<int> vec = {1, 2, 3, 4};
for (auto it = vec.begin(); it != vec.end(); ) {
    if (*it == 2) {
        // 在循环条件后立即更新迭代器,确保迭代器始终指向容器中的有效位置。
        it = vec.insert(it, 99);
    }
    if (*it == 3) {
        it = vec.erase(it);
    } else {
        ++it;
    }
}
错误示例(容量变化)

在容器上执行可能改变其容量的操作,如push_back,也可能导致迭代器失效:

vector<int> vec = {1, 2, 3, 4};
auto it = vec.begin();
vec.push_back(5); // 可能导致重新分配内存,使所有迭代器失效

问题分析:当容器需要重新分配内存时,所有现有迭代器将失效。

正确用法(检查迭代器有效性)

在执行可能导致容器重新分配内存的操作后,应检查并重新获取迭代器:

vector<int> vec = {1, 2, 3, 4};
auto it = vec.begin();
vec.push_back(5); // 可能导致重新分配内存
if (it != vec.end()) {
    // 迭代器仍然有效,可以继续使用
} else {
    // 迭代器失效,重新获取迭代器
    it = vec.begin();
}

关键点:通过比较迭代器与end()来判断其有效性,并在必要时重新初始化迭代器。

  • 27
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值