1.STL
- STL是什么呢?
C++ 标准模板库(Standard Template Library,简称 STL)是 C++ 标准库的重要组成部分,提供了一套常用的模板类和函数,用于数据存储、操作和算法的实现。
STL 是 C++ 强大的基础库,使得开发者能够高效、灵活地处理复杂的数据结构和算法。
注:string在STL之前创造,所以严格来说string不属于STL,但是STL的容器设计的风格也参考了string(或说保持风格统一),所以在我们学习过程中会感觉STL的容器会有string的影子。
1.1STL的组成
STL 通常被描述为由六大组成部分构成:
1. 容器(Containers)
2. 算法(Algorithms)
3. 迭代器(Iterators)
4. 仿函数(Functors)
5. 配接器(适配器)(Adapters)
6. 空间配置器(Allocators)
接下来我们将依次学习 STL 的各个部分,以容器为大纲逐步深入学习各部分。
2.vector
首先 什么是容器?什么是vector呢?
简单点理解STL中用来储存数据的类就是容器。
是 C++ 标准模板库(STL)中的一种顺序容器。它实现了动态数组,能够自动调整大小并高效管理内存。
vectorstd::vector
是 C++ 程序员最常用的容器之一,因为它既简单易用,又具备强大的功能。(可简单点理解成 vector就是一个动态数组,用vector这个类封装)
同样vector中详细的成员函数用法参考网站: cplusplus.com/reference/vector/vector/
2.1vector主要特点
-
动态大小:
std::vector
可以自动调整其大小,当元素插入时,vector
会自动分配更多的内存,以容纳新元素。(自动扩容)- 这与普通数组不同,普通数组在定义时必须指定大小且无法动态调整。
-
随机访问:
std::vector
支持常数时间的随机访问,即可以通过索引快速访问任意元素,类似于数组。- 例如,
vec[i]
可以快速访问第i
个元素。(和C语言的数组一样方式访问)
-
自动管理内存:
std::vector
会自动管理内存的分配和释放,因此你不需要手动管理内存,也不容易出现内存泄漏的问题。(动态内存,不需要自己申请和释放)
-
高效的插入和删除:
std::vector
在末尾添加或删除元素的操作是常数时间复杂度O(1)
,非常高效。(可频繁尾插尾删)- 但在中间或开头插入或删除元素的操作可能会导致大量数据移动,时间复杂度为
O(n)
,不如链表高效。
(vector底层实现是数据结构中顺序表,所以具备的特点也是顺序表的特点)
2.2 vector的使用
先看看一段代码:
#include <iostream>
#include <vector> //使用vector需要包的头文件
int main() {
// 创建一个空的 vector,vector中存放的数据类型是int,实例化为vec
std::vector<int> vec;
// 向 vec中添加元素
vec.push_back(10); // 添加 10
vec.push_back(20); // 添加 20
vec.push_back(30); // 添加 30
// 使用索引访问和修改元素
std::cout << "vec[0]: " << vec[0] << std::endl; // 打印第0个元素
vec[0] = 100; // 修改第0个元素为 100
std::cout << "vec[0]: " << vec[0] << std::endl; // 打印修改后的第0个元素
// 获取并打印 vec 的大小和容量
std::cout << "vec.size: " << vec.size() << std::endl; // 输出 3
std::cout << "vec.capacity: " << vec.capacity() << std::endl; // 输出大于或等于 3
// 遍历 vec 中的所有元素
std::cout << "Elements in vec : ";
for (size_t i = 0; i < vec.size(); ++i)
{
std::cout << vec[i] << " ";
}
std::cout << std::endl;
// 使用迭代器遍历 vec 中的所有元素
std::cout << "Elements in vector (using iterator): ";
for (auto it = vec.begin(); it != vec.end(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
// 删除末尾元素
vec.pop_back(); // 删除 30
// 再次遍历并打印修改后的 vec
std::cout << "Elements in vector : ";
for (auto it = vec.begin(); it != vec.end(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
运行结果:
vec[0]: 10
vec[0]: 100
vec.size: 3
vec.capacity: 3
Elements in vec : 100 20 30
Elements in vector (using iterator): 100 20 30
Elements in vector : 100 20
以上代码演示的是一个储存<int>类型数据的顺序表(vector),结合图形和代码一起看更加直观:
1.创建一个空vector
std::vector<int> vec;
2.尾插3个数据
vec.push_back(10);
vec.push_back(20);
vec.push_back(30);
3.修改和访问第0个数据
vec[0] = 100;
4.打印数据表中数据的长度和容量的大小
std::cout << "vec.size: " << vec.size() << std::endl;
std::cout << "vec.capacity: " << vec.capacity() << std::endl;
根据数据结果来看,在vector底层运作,他是动态申请的容量和数据长度一样大的,所以图形:
5.用迭代器遍历vector
std::cout << "Elements in vector (using iterator): ";
for (auto it = vec.begin(); it != vec.end(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
vec.end()
返回的是指向 vector
中最后一个元素的下一个位置的迭代器。这个位置实际上是一个“哨兵”位置(sentinel),不存储有效数据,通常用来标识容器的末尾。
这最后一个哨兵卫不会计入capacity(容量)内 。和string相同,vector
的迭代器底层也是用指针来实现,所以begin()返回的迭代器就是vec[0]的地址,end()同理返回的也是地址,且vector
内数据的地址是连续的。
6.删除一个数据
vec.pop_back(); // 删除 30
2.3进阶用法
通过2.2的学习,大家会发现,vector其实和string还是有很多相似之处的。
2.3.1.vector构造函数:
可以怎么样创建vector对象呢?常用的在以下列举出来了:(需注意的是第三种用法)
#include <iostream>
#include <vector>
using namespace std;
int main()
{
/*1*/
vector<int> vec1; // 创建一个空的 vector<int>
/*2*/
vector<int> vec2(5, 10); // 创建一个包含 5 个 10 的 vector<int>,即 {10, 10, 10, 10, 10}
vector<int> vec3(5); // 创建一个包含 5 个默认值 0 的 vector<int>,即 {0, 0, 0, 0, 0}
/*3*/
//template <class InputIterator> std::vector<T> vec(InputIterator first, InputIterator last);
//使用 [first, last) 范围内的元素创建一个 vector。(用区间初始化,传的是迭代器 vector迭代器就是地址)
int arr[] = { 1, 2, 3, 4, 5 };
vector<int> vec4(arr, arr + 5); // 使用数组中的元素构造 vector,即 {1, 2, 3, 4, 5}
/*4*///拷贝构造
vector<int> vec5(vec1); // 复制 vec1 的内容到 vec2 中
/*5*/
vector<int> vec6 = { 1, 2, 3, 4, 5 }; // 直接使用初始化列表创建 vector
/*6*///比较少用
vector<int> vec7 = { 1, 2, 3 };
vector<int> vec8(std::move(vec1)); // 移动 vec1 的内容到 vec2 中,vec1 被清空
}
2.3.2.vector各个常用成员函数:
接下介绍的成员函数仅常用,并不是所有,如果想要看更详细的函数用法请参考vector章节开始位置的网址。
1.push_back()
:(尾插)
- 功能:在
vector
的末尾添加一个元素。(用法同string的push_back)
2.pop_back():(尾删)
- 功能:移除
vector
末尾的元素。
3.size()
:
- 功能:返回
vector
中元素的数量。
4.capacity():
- 功能:返回
vector
当前分配的存储容量(即不需要重新分配内存可以容纳的元素数量)。
(因为上面2.2的代码已经演示过了这里不过多赘述)
5.empty():
- 功能:检查
vector
是否为空
std::vector<int> vec;
//如果为空返回bool的true ,不为空返回false
//语法:bool empty() const;(库中声明)
if (vec.empty())
{
std::cout << "Vector is empty." << std::endl;
}
6.clear()
:
- 功能:移除
vector
中的所有元素,但不释放内存。
std::vector<int> vec = {1, 2, 3};
vec.clear(); // 清空 vec 中的所有元素
7.insert()
:
- 功能:在指定位置插入元素。(如果所选下标有元素,将在该下标插入值,把原本在该下标及其后面的元素向后移动)
- 语法:
iterator insert(iterator pos, const T& value); // 在 pos 位置插入 value(pos)是下标
iterator insert(iterator pos, size_t n, const T& value); // 在 pos 位置插入 n 个 value
- 使用:
std::vector<int> vec = {1, 2, 4};
vec.insert(vec.begin() + 2, 3); // 在开始2位置插入 3,也就是vec[2] vec 变为 {1, 2, 3, 4}
vec.insert(vec.begin(),5, 3); // 在开始位置插入 3,vec 变为 {1, 2, 3, 4}
8.erase()
:
- 功能:移除指定位置或范围内的元素。
- 语法:
iterator erase(iterator pos); // 移除 pos 位置的元素(下标)
iterator erase(iterator first, iterator last); // 移除 [first, last) 范围内的元素
- 使用:
iterator erase(iterator pos); // 移除 pos 位置的元素(下标)
iterator erase(iterator first, iterator last); // 移除 [first, last) 范围内的元素
std::vector<int> vec = {1, 2, 3, 4};
vec.erase(vec.begin() + 2); // 移除索引 2 位置的元素,vec 变为 {1, 2, 4}
vec.erase(vec.begin() ,vec.end()); // 移除全部元素
9.resize()
:
- 功能:调整
vector
的大小。如果新大小大于当前大小,则用默认值或指定的值填充新元素。如果新大小小于当前大小则截断(超出新大小的元素将被移除) - 语法:
void resize(size_t n); //调整大小
void resize(size_t n, const T& value);//如果是扩容,那新开的空间初始化为value
- 用法
std::vector<int> vec = {1, 2, 3};
vec.resize(5); // 调整大小为 5,vec 变为 {1, 2, 3, 0, 0}
vec.resize(7, 10); // 调整大小为 7,vec 变为 {1, 2, 3, 0, 0, 10, 10}
vec.resize(2); //调整大小为 2,vec 变为 {1, 2}
10.reserve()
:
- 功能:预留足够的空间以容纳
n
个元素,避免在插入元素时频繁重新分配内存。 - 语法:
语法:void reserve(size_t n);
n
:表示你要预留的最小容量(即要分配的内存大小)。如果 n
大于当前容量,vector
将重新分配内存来达到指定的容量。如果 n
小于或等于当前容量,则不会发生任何变化。
- 用法:
std::vector<int> vec;
vec.reserve(10); // 预留空间以容纳 10 个元素
注意:
reserve
函数为vector
分配至少n
个元素的存储空间,而不会改变vector
的大小(即不会影响size()
的返回值)。这个函数只影响vector
的容量(capacity()
),不影响元素的数量。
reserve和resize的区别:
reserve
只影响capacity
,不改变size
,它只是预留内存空间。resize
影响size
和capacity
,改变vector
中元素的数量。如果size
增大,resize
会插入新元素,如果size
减小,resize
会删除多余的元素。
进阶代码演示:
#include <iostream>
#include <vector>
#include <algorithm> // 包含标准算法,如 sort 和 reverse
using namespace std;
int main() {
// 创建并初始化一个 vector
vector<int> numbers = { 10, 20, 30, 40, 50 };
// 使用迭代器遍历并输出 vector 的元素
cout << "numbers: ";
vector<int>::iterator it; //定义一个vector<int>类型的迭代器 变量it
for (it = numbers.begin(); it != numbers.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
// 在 vector 中插入元素
numbers.insert(numbers.begin() + 2, 25); // 在下标位置为 2 插入 25
cout << "numbers.insert(numbers.begin() + 2, 25): ";
for (const auto& num : numbers) //范围for打印
{
cout << num << " ";
}
cout << endl;
// 删除 vector 中的某个元素
numbers.erase(numbers.begin() + 3); // 删除下标位置为 3 的元素
cout << "numbers.erase(numbers.begin() + 3): ";
for (const auto& num : numbers) //范围for打印
{
cout << num << " ";
}
cout << endl;
// 使用标准算法对 vector 排序,sort是一种一种混合排序算法,它结合了 快速排序、堆排序 和 插入排序 的优点
//sort在C++ 标准库中 std::sort
sort(numbers.begin(), numbers.end(), greater<int>()); // 降序排序
cout << "std::sort: ";
for (const auto& num : numbers) //打印数组
{
cout << num << " ";
}
cout << endl;
//find也是C++ 标准库中的函数:std::find
vector<int>::iterator it1 = find(numbers.begin(), numbers.end(), 30); // 查找 vector 中的元素,返回迭代器,也就是元素所在的位置(地址)
if (it1 != numbers.end())
{
cout << "30 found at position: " << distance(numbers.begin(), it) << endl;
//std::distance 函数:用于计算两个迭代器之间的距离,也就是从第一个迭代器 numbers.begin() 走到第二个迭代器 it 需要经过的元素数量。
//这可以被看作是计算元素在 vector 中的位置(基于 0 的索引)。
}
else {
cout << "30 not found." << endl;
}
//reverse 也在C++ 标准库中 std::reverse()标准库函数
// 反转 vector 的元素顺序
reverse(numbers.begin(), numbers.end());
cout << "reverse (): ";
for (const auto& num : numbers) //打印
{
cout << num << " ";
}
cout << endl;
return 0;
}
输出结果:
numbers: 10 20 30 40 50
numbers.insert(numbers.begin() + 2, 25): 10 20 25 30 40 50
numbers.erase(numbers.begin() + 3): 10 20 25 40 50
std::sort: 50 40 25 20 10
30 not found.
reverse (): 10 20 25 40 50
代码解析都在上面代码的注释里,这里就不再赘述。这段代码展示了如何使用 C++ 标准库中的 vector
容器和常用算法函数。通过使用iterator迭代器进行遍历、插入和删除操作,同时使用了 sort
、find
和 reverse
等算法函数,对 vector
进行排序、查找和反转操作。理解这些操作对于学习 std::vector
和STL组成部分之一的算法非常有帮助。