目录

0.Vector文档介绍

1.Vector 基本概念

主要特性:

Vector 的优缺点

优点:

缺点:

3. Vector 的内存模型

4.vector常用接口的使用

4.1vector的常见构造(初始化)

4.2vector的遍历及迭代器

operator[]的使用

at()的使用

迭代器分类

迭代器操作

迭代器失效规则

范围for

4.3vector 空间增长问题

reserve() - 预分配空间

resize() - 调整大小

4.4vector 增删查改

push_back:

pop_back:

find:

insert:

erase:

swap:


0.Vector文档介绍

1.Vector 基本概念

vector 是 C++ 标准模板库(STL)中的一个序列容器,提供动态数组的功能。

使用连续存储位置来存储其元素,向量的大小可以动态变化,其存储由容器自动管理

与其他动态序列容器(双端队列、列表和前向列表)相比,向量在访问元素时效率极高(与数组类似),且在末尾添加或删除元素时也相对高效。然而,对于涉及在非末尾位置插入或删除元素的操作,其性能不如其他容器,并且其迭代器和引用的稳定性也低于列表和前向列表。

主要特性:


动态扩容:自动管理内存,根据需要动态调整大小

随机访问:支持 O(1) 时间复杂度的元素访问

连续存储:元素在内存中连续存储

模板类:可以存储任意类型的数据

一句话总结:

Vector = 动态数组 + 自动内存管理 + 快速随机访问 - 中间操作代价

Vector 的优缺点

优点:
  1. 随机访问快:O(1)时间复杂度
  2. 缓存友好: 内存连续,CPU预取效率高
  3. 尾部操作高效: push_back/pop_back平均O(1)
  4. 内存紧凑: 无额外指针开销
  5. 与C数组兼容: 可直接获取底层指针
缺点:
  1. 中间插入删除慢: O(n)时间复杂度,需要移动元素
  2. 扩容成本高: 需要重新分配内存和拷贝数据
  3. 容量可能浪费: 预分配的空间可能用不完
  4. 迭代器易失效: 插入删除或扩容后迭代器可能失效
  5. 头部操作效率低: 在开头插入删除需要移动所有元素

3. Vector 的内存模型

template<class T>
class vector {
private:
    T* start;          // 指向数据块的开始
    T* finish;         // 指向最后一个有效元素的下一个位置
    T* end_Of_storage;   // 指向存储容量的尾
};

4.vector常用接口的使用

4.1vector的常见构造(初始化)

无参构造函数 vector()

vector<int> v;  // 创建空vector

填充构造函数 vector(n, val)

vector<int> v(5, 10);  // 5个10
vector<int> v(3);      // 3个0(默认值)

拷贝构造函数 vector(const vector& x)

vector<int> v1 = {1, 2, 3};
vector<int> v2(v1);    // 深拷贝

范围构造函数 vector(first, last)

int arr[] = {1, 2, 3};
vector<int> v(arr, arr + 3);  // 从数组构造

vector<int> source = {1, 2, 3, 4};
vector<int> v2(source.begin(), source.end() - 1);  // 从vector子范围构造

4.2vector的遍历及迭代器

遍历方式 优点 缺点 适用场景
operator[] 简单直观,可随机访问 需要知道size() 需要索引或随机访问
迭代器 灵活,支持算法,通用 语法稍复杂 需要修改元素或使用算法
范围for 简洁,易读 不能控制步长 简单遍历,C++11以上
at 边界检查,异常抛出 有检查开销 用户输入、不确定的索引
operator[]的使用

operator[] 是 vector 最高效的访问方式,但需要程序员自己保证索引的有效性。在性能关键的代码和已知索引安全的场景中应该优先使用。

// 性能关键循环
for (size_t i = 0; i < vec.size(); ++i) {
    result += vec[i] * weights[i];  // 快速无检查
}

// 已知索引有效
void update_element(vector<int>& vec, size_t index, int value) {
    if (index < vec.size()) {  // 手动检查
        vec[index] = value;    // 然后安全使用operator[]
    }
}

// 数学计算
vector<double> normalize(const vector<double>& data) {
    vector<double> result(data.size());
    for (size_t i = 0; i < data.size(); ++i) {
        result[i] = data[i] / data[0];  // 快速访问
    }
    return result;
}
at()的使用

at() 函数提供了安全的元素访问机制,特别适合处理不可信的输入数据或在需要健壮性的场景中使用。

// 处理用户输入
int user_index = get_user_input();
try {
    string value = data.at(user_index);
    process(value);
} catch (const out_of_range& e) {
    show_error("无效的索引");
}

// 处理外部数据
vector<int> process_indices = get_external_indices();
for (int idx : process_indices) {
    try {
        result += data.at(idx);
    } catch (const out_of_range& e) {
        log_error("跳过无效索引: " + to_string(idx));
    }
}

vector iterator 的使用

iterator的使用 接口说明
begin + end(重点) 获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置 的iterator/const_iterator
rbeginrend 获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的 reverse_iterator

迭代器分类

通过begin函数可以得到容器中第一个元素的正向迭代器,通过end函数可以得到容器中最后一个元素的后一个位置的正向迭代器

通过rbegin函数可以得到容器中最后一个元素的反向迭代器,通过rend函数可以得到容器中第一个元素的前一个位置的反向迭代器

vector<int> vec = {1, 2, 3, 4, 5};

// 正向迭代器
vector<int>::iterator it = vec.begin();        // 可读写
vector<int>::const_iterator cit = vec.cbegin(); // 只读

// 反向迭代器  
vector<int>::reverse_iterator rit = vec.rbegin();        // 可读写反向
vector<int>::const_reverse_iterator crit = vec.crbegin(); // 只读反向
迭代器操作
vector<int>::iterator it = vec.begin();

// 基本操作
*it;           // 解引用
++it;          // 前进到下一个
--it;          // 后退到上一个
it + 3;        // 前进3个位置
it1 == it2;    // 比较是否指向同一位置
it1 < it2;     // 比较位置前后

// 特殊位置
vec.begin();   // 指向第一个元素
vec.end();     // 指向最后一个元素的下一个位置
vec.rbegin();  // 指向最后一个元素
vec.rend();    // 指向第一个元素的前一个位置
迭代器失效规则

插入操作

vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin() + 2;  // 指向3

// 插入可能导致重新分配
vec.push_back(6);
// it 可能失效!不要继续使用

// 安全做法:重新获取迭代器
it = vec.begin() + 2;

删除操作

vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin() + 2;  // 指向3

vec.erase(vec.begin() + 1);  // 删除2
// it 现在失效!指向的位置可能已改变

// 安全做法:使用erase返回值
it = vec.erase(vec.begin() + 1);
范围for
for (range_declaration : range_expression) {
    // 循环体
}
//range_declaration:声明一个变量,用于存储范围中的每个元素
//range_expression:任何可以返回序列的表达式(vector、array、string等)

简单演示:(遍历v中的变量)

int main()
{
	vector<int> v(2, 10);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

范围for循环在编译时会被展开为传统的迭代器代码:

// 范围for
for (const auto& element : container) {
    // 处理element
}

// 等价于
{
    auto&& __range = container;
    auto __begin = begin(__range);
    auto __end = end(__range);
    for (; __begin != __end; ++__begin) {
        const auto& element = *__begin;
        // 处理element
    }
}

4.3vector 空间增长问题

容量空间 接口说明
size 获取数据个数
capacity 获取容量大小
empty 判断是否为空
resize(重点) 改变vector的size
reserve(重点) 改变vector的capacity
  • capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义 的。vs是PJ版本STL,g++是SGI版本STL。
  • reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问 题。
  • resize在开空间的同时还会进行初始化,影响size。
reserve() - 预分配空间
vector<int> vec;
vec.reserve(100);  // 预分配100个元素的空间

// 内部实现大致如下:
void reserve(size_t n) {
    if (n > capacity()) {
        // 1. 分配新内存
        // 2. 拷贝现有元素
        // 3. 释放旧内存
        // 4. 更新指针
    }
}
resize() - 调整大小
vector<int> vec;
vec.resize(10);     // size=10, capacity>=10
vec.resize(5);      // size=5, capacity不变
vec.resize(20, 1);  // size=20, 新元素初始化为1

4.4vector 增删查改

vector增删查改 接口说明
push_back(重点) 尾插
pop_back(重点) 尾删
find 查找。(注意这个是算法模块实现,不是vector的成员接口)
insert 在position之前插入val
erase 删除position位置的数据
swap 交换两个vector的数据空间
push_back:

void push_back(const T& value);

这个接口的功能很明确,就是在尾部插入数据

int main()
{
	vector<int> v;
	for (int i = 0; i < 5; i++)
	{
		v.push_back(i);
	}
	for (auto ch : v)
	{
		cout << ch << " ";
	}
	cout << endl;
	return 0;
}
//0 1 2 3 4
pop_back:

void pop_back();

相反,这个接口就是去尾删最后一个元素

int main()
{
	vector<int> v(5, 2);
	for (auto ch : v)
	{
		cout << ch << " ";
	}
	cout << endl;
	for (int i = 0; i < 3; i++)
	{
		v.pop_back();
	}
	for (auto ch : v)
	{
		cout << ch << " ";
	}
 
	cout << endl;
	return 0;
}
//2 2 2 2 2
  2 2
find:

template<class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& value);

这个接口是封装在了 <algorithm> 这个头文件中的一种算法。

返回一个迭代器,指向范围[first,last)中与val相等的第一个元素如果没有找到这样的元素,则函数返回last
该函数使用运算符==将单个元素与val进行比较。

常用于我们要去删除一个指定的数据,传入指定的搜索区间和要查找的值,若是返回的迭代器位置没有到达末尾的话,代表找到了这个值,我们去删除这个迭代器即可

int main()
{
	int a[] = { 1,2,7,4,5,1,2,5,7,6 };
	vector<int> v(a, a + 10);
 
	vector<int>::iterator pos = find(v.begin(), v.end(), 7);
	if (pos != v.end())
	{
		v.erase(pos);
	}
 
	for (auto ch : v)
	{
		cout << ch << " ";
	}
	cout << endl;
	return 0;
}
//1 2 4 5 1 2 5 7 6
insert:

对于insert这个接口,这里只介绍较为常见的两个重载

iterator insert (const_iterator position, const value_type& val);

在指定位置插入一个元素,返回指向新插入元素的迭代器

int main() {
    vector<int> vec = {10, 20, 30, 40};
    
    cout << "插入前: ";
    for (int n : vec) cout << n << " ";
    cout << endl;
    
    // 在位置2插入25
    auto it = vec.insert(vec.begin() + 2, 25);
    
    cout << "插入后: ";
    for (int n : vec) cout << n << " ";
    cout << endl;
    
    cout << "返回值指向: " << *it << endl;
    cout << "新元素位置: " << (it - vec.begin()) << endl;
    
    return 0;
}
//
插入前: 10 20 30 40 
插入后: 10 20 25 30 40 
返回值指向: 25
新元素位置: 2

iterator insert (const_iterator position, size_type n, const value_type& val);

在 vector 的指定位置插入 n 个相同的元素,返回指向第一个新插入元素的迭代器

int main() {
    vector<int> vec = {1, 2, 3, 4};
    
    cout << "插入前: ";
    for (int n : vec) cout << n << " ";
    cout << endl;
    
    // 在位置1插入3个99
    auto it = vec.insert(vec.begin() + 1, 3, 99);
    
    cout << "插入后: ";
    for (int n : vec) cout << n << " ";
    cout << endl;
    
    cout << "返回值指向: " << *it << endl;
    cout << "插入了 " << 3 << " 个 " << 99 << endl;
    
    return 0;
}
//
插入前: 1 2 3 4 
插入后: 1 99 99 99 2 3 4 
返回值指向: 99
插入了 3 个 99
erase:

iterator erase (const_iterator position);//删除单个元素
iterator erase (const_iterator first, const_iterator last);//删除范围元素

int main() {
    vector<int> vec = {10, 20, 30, 40, 50, 60};
    
    cout << "原始: ";
    for (int n : vec) cout << n << " ";
    cout << endl;
    
    // 1. 删除单个元素(位置2的元素30)
    vec.erase(vec.begin() + 2);
    cout << "删除位置2后: ";
    for (int n : vec) cout << n << " ";
    cout << endl;
    
    // 2. 删除范围元素(位置1到3)
    vec.erase(vec.begin() + 1, vec.begin() + 3);
    cout << "删除位置1-3后: ";
    for (int n : vec) cout << n << " ";
    cout << endl;
    
    return 0;
}
//
原始: 10 20 30 40 50 60 
删除位置2后: 10 20 40 50 60 
删除位置1-3后: 10 50 60 
swap:

void swap (vector& x);

顾名思义,这个接口的功能就是快速交换两个 vector 的内容。

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> vec1 = {1, 2, 3};
    vector<int> vec2 = {10, 20, 30, 40};
    
    cout << "交换前:" << endl;
    cout << "vec1: ";
    for (int n : vec1) cout << n << " ";
    cout << " | size: " << vec1.size() << endl;
    
    cout << "vec2: ";
    for (int n : vec2) cout << n << " ";
    cout << " | size: " << vec2.size() << endl;
    
    // 交换两个vector
    vec1.swap(vec2);
    
    cout << "\n交换后:" << endl;
    cout << "vec1: ";
    for (int n : vec1) cout << n << " ";
    cout << " | size: " << vec1.size() << endl;
    
    cout << "vec2: ";
    for (int n : vec2) cout << n << " ";
    cout << " | size: " << vec2.size() << endl;
    
    return 0;
}
//
交换前:
vec1: 1 2 3  | size: 3
vec2: 10 20 30 40  | size: 4

交换后:
vec1: 10 20 30 40  | size: 4
vec2: 1 2 3  | size: 3

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐