目录

一,成员函数分类表

表格说明

二,使用细节(常用的)

1. 构造和遍历方式 

a.构造:

b.遍历:

2. string和vector

3. push_back和emplace_back()对比

三,迭代器失效

1. 迭代器失效的原因

2. 迭代器失效的具体场景

插入元素导致迭代器失效

删除元素导致迭代器失效

3. 避免迭代器失效的方法

插入元素时

删除元素时

4. 总结


本质std::vector 是一个封装了动态大小数组的顺序容器,它可以存储任意类型的元素,并且能够在运行时动态地调整大小。如果是元素很少不会向内存申请空间,直接用存放,如果元素多就会像内存申请空间(有很多底层和string相似,可以看看我发的string)。

以下是按照标准Markdown格式整理的表格结构,内容基于C++ vector的成员函数分类:

一,成员函数分类表

类别 函数名称 功能描述
构造/析构 vector() 构造空的vector容器
~vector() 析构容器并释放内存
赋值 operator= 用另一个vector的内容替换当前内容
迭代器 begin() 返回指向第一个元素的迭代器
end() 返回指向末尾(尾后)的迭代器
rbegin() 返回指向反向起始的反向迭代器
rend() 返回指向反向末尾的反向迭代器
cbegin() 返回const版本的起始迭代器
cend() 返回const版本的末尾迭代器
crbegin() 返回const反向起始迭代器
crend() 返回const反向末尾迭代器
容量 size() 返回当前元素数量
max_size() 返回可容纳的最大元素数量
resize(n) 调整容器大小为n个元素
capacity() 返回当前分配的存储空间大小
empty() 检查容器 是否为空
reserve(n) 预分配至少能容纳n个元素的存储空间
shrink_to_fit() 请求移除未使用的容量
元素访问 operator[] 通过下标访问元素(无边界检查)
at() 通过下标访问元素(带边界检查)
front() 访问第一个元素
back() 访问最后一个元素
data() 返回指向底层数组的指针
修改器 assign() 用指定值替换容器内容
push_back() 在末尾添加元素
pop_back() 移除末尾元素
insert() 在指定位置插入元素
erase() 移除指定位置的元素
swap() 交换两个容器的内容
clear() 清空容器内容
emplace() 在指定位置原位构造元素
emplace_back() 在末尾原位构造元素
表格说明
  1. 表格按功能类别分组,便于快速查找特定操作
  2. 所有成员函数均为public访问权限
  3. 函数命名遵循C++标准库惯例,括号()表示函数调用
  4. 元素访问类函数区分了边界检查行为(at() vs operator[])

二,使用细节(常用的)

1. 构造和遍历方式 
a.构造:

C++98时候有4种构造方式:1,直接构造(就是不传参).2,传一个n和一个你要的元素x,会构造出n个x(底层和resize函数差不多).3,迭代器构造(实际底层是指针用迭代器封装).4,拷贝构造。

C++11在基础上添加了两种,但是我只是介绍常用的:5,传{}构造(std::vector<int> i2({ 1,2,3,4,5,6,7 });)->底层是vector (initializer_list<value_type> il, const allocator_type& alloc = allocator_type());

现在介绍一下initializer_list<value_type> 是 C++ 标准库中的一个模板类,它提供了一种方便的方式来传递和使用初始化列表, 是一种轻量级的容器类型,它允许使用花括号 {} 来初始化对象,并且可以在函数参数、构造函数等地方使用初始化列表,也支持迭代器。

那现在开始看看这个5个构造:

//vector()
std::vector<int> i;
//vector(int n, const T& value = T())
std::vector<int> i1(10,1);
//vector(InputIterator first, InputIterator last)
std::vector<int> i3(i2.begin(),i2.end());
//vector(const vector<T>&v)
std::vector<int> i4(i3);
//vector(std::initializer_list<T> il, const T& value = T())
std::vector<int> i2({ 1,2,3,4,5,6,7 });
//vector<T>& operator= (const vector<T>& v)
i = i4;
b.遍历:

和string的遍历没有什么太大变化,但是我还要讲讲:

下标遍历:

for (size_t j = 0; j < i.size(); j++)
{
	std::cout << i[j] << " ";
}

while迭代器:

std::vector<int>::const_iterator x = i.begin();
while (x != i.end())
{
	std::cout << *x << " ";
	x++;
}

范围for:

for (auto& e : s)
{
	std::cout << e << " ";
}
std::cout << std::endl;
2. string和vector<char>

string和vector<char>底层没有什么区别,都是用char数组存数据,但是string比vector<char>比较方便,比vector<char>多一些接口函数方便处理字符串。

3. push_back和emplace_back()对比

基本功能
push_back() 将一个已存在的元素添加到 vector 末尾。
emplace_back() 直接在 vector 末尾构造新元素,无需预先创建对象。

参数传递
push_back() 接受已构造的对象作为参数,自定义类型需先实例化再传入。
emplace_back() 接受构造对象所需的参数列表,直接在容器内构造对象。

性能表现
push_back() 对基本类型性能接近 emplace_back();对自定义类型可能触发复制/移动操作,大对象开销较高。//可以传{},不能直接传值。
emplace_back() 直接在目标内存构造对象,避免临时对象的复制或移动,尤其对自定义类型更高效。//不能传{}插入,直接传值就可以了。

std::vector<std::pair<int, int>> s;
s.push_back({ 1, 2 });
s.emplace_back(1, 2);
//如果反过来会报错。

异常安全性
两者均保证异常发生时容器状态不变。push_back() 在元素已存在时触发异常;emplace_back() 在构造过程中发生异常时对象未完全创建。

适用场景
push_back() 适用于已有现成对象需插入容器的场景。
emplace_back() 适合直接构造新对象并插入,尤其是需避免冗余复制/移动操作的自定义类型。

三,迭代器失效

1. 迭代器失效的原因

迭代器失效主要是由于 vector 的内存布局发生改变所导致的。vector 中的元素在内存中是连续存储的,当对 vector 进行某些操作时,可能会导致内存重新分配或者元素位置移动,从而使得原来的迭代器不再指向正确的元素。

2. 迭代器失效的具体场景

插入元素导致迭代器失效
  • 尾部插入(push_back 或 emplace_back
    • 原理:当 vector 的容量不足时,插入元素会触发内存重新分配。新的内存空间会被分配,原有元素会被复制或移动到新的内存中,原来的迭代器就会失效。
    • 示例
std::vector<int> vec = {1, 2, 3}; 
auto it = vec.begin(); 
// 插入元素,可能导致内存重新分配 
vec.push_back(4); 
// 此时 it 可能已经失效 
std::cout << *it << std::endl;
  • 中间插入(insert
    • 原理:在 vector 中间插入元素时,插入位置之后的所有元素都会向后移动,这会导致插入位置及之后的迭代器失效。
    • 示例

std::vector<int> vec = {1, 2, 3}; 
auto it = vec.begin() + 1; 
// 在 it 位置插入元素 
vec.insert(it, 4); 
// 此时 it 及之后的迭代器失效 
std::cout << *it << std::endl;

删除元素导致迭代器失效
  • 尾部删除(pop_back
    • 原理:删除 vector 的最后一个元素,会使指向最后一个元素的迭代器失效。
    • 示例

std::vector<int> vec = {1, 2, 3}; 
auto it = vec.end() - 1; 
// 删除最后一个元素 
vec.pop_back();
 // 此时 it 失效 
std::cout << *it << std::endl;
  • 中间删除(erase
    • 原理:删除 vector 中间的元素时,删除位置之后的所有元素都会向前移动,这会导致删除位置及之后的迭代器失效。
    • 示例
std::vector<int> vec = {1, 2, 3};
 auto it = vec.begin() + 1; 
// 删除 it 位置的元素
 vec.erase(it); 
// 此时 it 及之后的迭代器失效
 std::cout << *it << std::endl;

3. 避免迭代器失效的方法

插入元素时
  • 记录插入位置:在插入元素之前,记录插入位置的偏移量,插入后重新计算迭代器。
std::vector<int> vec = {1, 2, 3}; 
auto it = vec.begin() + 1; 
size_t offset = it - vec.begin(); 
vec.insert(vec.begin() + offset, 4); 
it = vec.begin() + offset; 
// 重新计算迭代器 
std::cout << *it << std::endl;

删除元素时
  • 使用 erase 的返回值erase 方法会返回删除元素之后的下一个有效迭代器,可以直接使用这个返回值更新迭代器。
std::vector<int> vec = {1, 2, 3}; 
auto it = vec.begin() + 1;
 it = vec.erase(it); 
// 使用 erase 的返回值更新迭代器 
std::cout << *it << std::endl;

4. 总结

在使用 vector 的迭代器时,要时刻注意迭代器失效的问题。在进行插入或删除操作后,及时更新迭代器,避免使用失效的迭代器,以确保程序的正确性和稳定性。

更多推荐