1.vector 的介绍及使用
1.1 vector 的介绍
vector 的文档介绍
-
vector 是表示可变大小数组的序列容器。
-
就像数组一样,vector 也采用的连续存储空间来存储元素。也就是意味着可以采用下标对 vector 的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
-
本质讲,vector 使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector 并不会每次都重新分配大小。
-
vector 分配空间策略:vector 会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
-
因此,vector 占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
-
与其它动态序列容器相比(deque, list and forward_list), vector 在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起 list 和 forward_list统一的迭代器和引用更好。
1.2 vector 的使用
vector 学习时一定要学会查看文档:vector 在实际中非常的重要,在实际中我们熟悉常 见的接口就可以,下面列出了哪些接口是要重点掌握的。
1.2.1 vector 的定义与构造函数
基本概念
vector
是 C++ 标准模板库 (STL) 中的序列容器,表示可以动态改变大小的数组,在 <vector>
头文件中定义。
构造函数详解
1. 无参构造函数 (重点)
vector();
-
创建一个空的 vector 容器,不包含任何元素
-
初始容量为 0,但在首次添加元素时会自动分配内存
-
示例:
vector<int> v1; // 创建一个空的整型vector
2.数量初始化构造函数
vector(size_type n, const value_type& val = value_type());
-
创建包含 n 个元素的 vector,每个元素初始化为 val
-
如果未提供 val,则使用值初始化(对于内置类型,如 int,初始化为 0)
-
示例:
vector<int> v2(5, 10); // 包含5个元素,每个都是10: [10,10,10,10,10]
vector<int> v3(3); // 包含3个元素,每个都是0: [0,0,0]
3.拷贝构造函数 (重点)
vector(const vector& x);
-
创建一个新 vector,其内容与参数 x 完全相同(深拷贝)
-
时间复杂度:O(n),需要复制所有元素
-
示例:
vector<int> original(3, 7); // [7,7,7]
vector<int> copy(original); // 创建副本: [7,7,7]
4.迭代器范围构造函数
template <class InputIterator>
vector(InputIterator first, InputIterator last);
-
使用迭代器范围 [first, last) 中的元素初始化 vector
-
可以接受任何类型的输入迭代器(数组指针、其他容器的迭代器等)
-
示例:
int arr[] = {1, 2, 3, 4, 5};
vector<int> v4(arr, arr + 5); // 使用数组初始化: [1,2,3,4,5]
list<int> myList = {10, 20, 30};
vector<int> v5(myList.begin(), myList.end()); // 使用list迭代器: [10,20,30]
1.2.2 vector iterator 的使用
迭代器基本概念
迭代器提供了一种统一的方法来访问容器中的元素,类似于指针的行为。
常规迭代器
1.begin() 和 end()
iterator begin() noexcept;
const_iterator begin() const noexcept;
iterator end() noexcept;
const_iterator end() const noexcept;
-
begin()
: 返回指向第一个元素的迭代器 -
end()
: 返回指向最后一个元素之后位置的迭代器(不是最后一个元素) -
const版本用于const对象,防止修改元素
-
示例:
vector<int> v = {10, 20, 30, 40};
// 使用迭代器遍历
for (auto it = v.begin(); it != v.end(); ++it) {
cout << *it << " "; // 输出: 10 20 30 40
}
// 修改元素
for (auto it = v.begin(); it != v.end(); ++it) {
*it += 5; // 每个元素加5
}
2.cbegin() 和 cend() (C++11)
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
-
返回const迭代器,即使vector本身不是const
-
确保不会通过迭代器修改元素
-
示例:
vector<int> v = {1, 2, 3};
auto it = v.cbegin(); // 只能读取,不能修改 *it
// *it = 10; // 错误: 不能修改
反向迭代器
1.rbegin() 和 rend()
reverse_iterator rbegin() noexcept;
const_reverse_iterator rbegin() const noexcept;
reverse_iterator rend() noexcept;
const_reverse_iterator rend() const noexcept;
-
rbegin()
: 返回指向最后一个元素的反向迭代器 -
rend()
: 返回指向第一个元素之前位置的反向迭代器 -
反向迭代器递增操作会向容器开头移动
-
示例:
vector<int> v = {10, 20, 30, 40};
// 反向遍历
for (auto rit = v.rbegin(); rit != v.rend(); ++rit) {
cout << *rit << " "; // 输出: 40 30 20 10
}
2.crbegin() 和 crend() (C++11)
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;
-
返回const反向迭代器,确保不会通过迭代器修改元素
迭代器类型总结
迭代器类型 | 函数 | 描述 |
正向迭代器 | begin(), end() | 从首元素向末尾遍历 |
常量正向迭代器 | cbegin(), cend() | 只读正向遍历 |
反向迭代器 | rbegin(), rend() | 从末元素向开头遍历 |
常量反向迭代器 | crbegin(), crend() | 只读反向遍历 |
注意事项
-
迭代器失效:当vector进行插入或删除操作时,可能会导致迭代器失效,需要重新获取
-
容量与大小:
size()
返回元素数量,capacity()
返回已分配内存可容纳的元素数量 -
性能特征:
-
随机访问:O(1)
-
末尾插入/删除:平均O(1)
-
中间插入/删除:O(n)
-
关键点总结
-
迭代器类型:
-
普通迭代器:可读写
-
常量迭代器:只读
-
反向迭代器:反向遍历,可读写
-
常量反向迭代器:反向遍历,只读
-
-
迭代器操作:
-
*iter
:解引用,访问元素 -
++iter
/--iter
:移动到下一个/上一个位置 -
iter + n
/iter - n
:随机访问(仅适用于随机访问迭代器)
-
-
注意事项:
-
修改容器大小(插入/删除)可能导致迭代器失效
-
使用常量迭代器可以防止意外修改
-
C++11 后推荐使用
auto
关键字简化迭代器声明
-