🎩 欢迎来到技术探索的奇幻世界👨💻
📜 个人主页:@一伦明悦-CSDN博客
✍🏻 作者简介: C++软件开发、Python机器学习爱好者
🗣️ 互动与支持:💬评论 👍🏻点赞 📂收藏 👀关注+
如果文章有所帮助,欢迎留下您宝贵的评论,
点赞加收藏支持我,点击关注,一起进步!
前言
STL(Standard Template Library)是C++标准库中的一个重要组成部分,提供了一系列通用的模板类和函数,用于实现常见的数据结构和算法。STL容器是STL中最常用的组件之一,用于存储和管理数据。以下是关于STL容器的详细介绍:
序列容器:
- vector:动态数组,支持随机访问和动态大小调整。
- deque:双端队列,支持在两端快速插入和删除元素。
- list:双向链表,支持高效的插入和删除操作。
关联容器:
- set:基于红黑树的有序集合。
- map:基于红黑树的有序映射(键-值对)。
- multiset:允许重复值的有序集合。
- multimap:允许重复键的有序映射。
无序关联容器:
- unordered_set:基于哈希表的无序集合。
- unordered_map:基于哈希表的无序映射。
- unordered_multiset:允许重复值的无序集合。
- unordered_multimap:允许重复键的无序映射。
容器适配器:
- stack:栈,基于deque或list实现。
- queue:队列,基于deque或list实现。
- priority_queue:优先队列,基于vector实现。
智能指针:
- shared_ptr:共享所有权的智能指针,基于引用计数实现。
- unique_ptr:独占所有权的智能指针,禁止复制。
- weak_ptr:弱引用智能指针,不增加引用计数。
STL容器提供了丰富的接口和算法,使得数据的存储、操作和管理变得更加便捷和高效。在使用STL容器时,可以根据具体需求选择合适的容器类型,并结合迭代器、算法等功能来完成复杂的数据处理任务。STL容器的广泛应用也使得C++成为一种强大的编程语言,适用于各种领域的开发和编程。
正文
01-STL基础知识
STL(Standard Template Library)是C++标准库的一部分,提供了一套通用的模板类和函数,用于实现常见的数据结构和算法。STL包括容器(Containers)、算法(Algorithms)和迭代器(Iterators)三大组件,为C++程序员提供了丰富、高效的数据结构和算法支持。
/* 长久以来,软件界一直希望建立一种可重复利用的东西
C++的面向对象和泛型编程思想,目的就是复用性的提升
大多情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作
为了建立数据结构和算法的一套标准,诞生了STL
*/// STL(Standard Template Library,标准模板库)
// STL广义上分为容器(container)、算法(algorithm)、迭代器(iterator)
// 容器和算法之间通过迭代器进行无缝连接。
// STL 几乎所有的代码都采用了模板类或者模板函数// STL六大组件:容器,算法,迭代器,仿函数,适配器,空间配置器
/* 1、容器分为两种 12354
序列式容器: 强调值的排序,序列式容器中的每个元素均有固定的位置。 12354 按初始位置
关联式容器: 二叉树结构,各元素之间没有严格的物理上的顺序关系 12345 按顺序排序
*/
/* 2、质变算法和非质变算法。 12345
质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等 1234 改变值
非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等 12345 并未改变*/
/* 3、提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
每个容器都有自己专属的迭代器,迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针双向迭代器 读写操作,并能向前和向后操作 读写,支持++、--,
随机访问迭代器 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器
读写,支持++、--、[n]、-n、<、<=、>、>=
常用的容器中迭代器种类为双向迭代器,和随机访问迭代器*/
容器(Containers):
-
容器是STL中最重要的组成部分之一,用于存储和管理数据。常见的STL容器包括 vector、deque、list、set、map 等。下面以 vector 和 map 容器为例进行具体的代码分析:
-
vector(动态数组):用于存储动态大小的元素序列,支持随机访问和动态增删操作,是使用最频繁的容器之一。
#include <iostream>
#include <vector>
using namespace std;int main() {// 创建一个存储 int 类型数据的 vectorvector<int> vec;// 向 vector 中添加数据vec.push_back(10);vec.push_back(20);vec.push_back(30);// 遍历 vector 中的数据for (int i = 0; i < vec.size(); ++i) {cout << vec[i] << " ";}return 0;
}
map(映射):用于存储键值对的集合,支持快速的查找和插入操作,适合用于建立键到值的映射关系。
#include <iostream>
#include <map>
using namespace std;int main() {// 创建一个存储 string 类型键和 int 类型值的 mapmap<string, int> myMap;// 插入键值对myMap["apple"] = 10;myMap["banana"] = 20;myMap["orange"] = 30;// 遍历 map 中的键值对for (const auto &pair : myMap) {cout << pair.first << ": " << pair.second << endl;}return 0;
}
算法(Algorithms):
- STL中还提供了丰富的算法函数,用于对容器中的数据进行操作,例如排序、查找、变换等。这些算法函数可以结合容器和迭代器进行灵活的数据处理。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;int main() {vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6, 5};// 对 vector 中的数据进行排序sort(vec.begin(), vec.end());// 遍历排序后的 vectorfor (int num : vec) {cout << num << " ";}return 0;
}
STL通过提供丰富、高效的容器和算法,简化了C++程序的开发过程,提高了代码的可读性和可维护性。程序员可以灵活运用STL中的容器和算法来处理数据,在实际开发中大大提高了开发效率。通过深入学习STL的使用,程序员能够更好地掌握C++编程技能,写出高质量的代码。
02-Vector数组-存放内置数据类型
Vector是C++标准库中的一种容器,用于存储一组同类型的元素,其中也可以存放内置数据类型。下面详细解释Vector数组存放内置数据类型的相关内容:
定义和声明:
- 使用vector需要包含头文件
<vector>
。- 可以通过声明
vector<数据类型> 变量名
来定义一个vector。初始化:
- 可以通过赋值语句、列表初始化、拷贝初始化等方式进行初始化。
插入和删除元素:
- 使用
push_back()
方法在vector末尾插入元素。- 使用
pop_back()
方法删除vector末尾的元素。- 使用
insert()
方法在指定位置插入元素。- 使用
erase()
方法删除指定位置或指定范围的元素。访问元素:
- 可以使用下标访问
[]
或at()
方法访问指定位置的元素。- 可以使用迭代器进行元素访问。
动态调整大小:
- Vector可以动态调整大小,即在运行时改变元素数量,使用
resize()
方法实现。内存管理:
- Vector自动管理内存,当元素数量超过当前容量时,会自动扩展容量以容纳更多元素。
常用操作:
size()
:返回vector中元素的个数。empty()
:判断vector是否为空。clear()
:清空vector中的所有元素。swap()
:交换两个vector的内容。
示例代码:
#include <iostream>
#include <vector>
using namespace std;int main() {// 定义一个存放整数的vectorvector<int> vec;// 向vector中插入元素vec.push_back(10);vec.push_back(20);vec.push_back(30);// 遍历vector并输出元素for (int i = 0; i < vec.size(); ++i) {cout << vec[i] << " ";}cout << endl;// 使用迭代器访问元素vector<int>::iterator it;for (it = vec.begin(); it != vec.end(); ++it) {cout << *it << " ";}cout << endl;// 删除末尾的元素vec.pop_back();// 重新遍历vector并输出元素for (int num : vec) {cout << num << " ";}cout << endl;return 0;
}
下面给出具体代码分析应用过程:这段代码主要展示了使用vector
存放内置数据类型(这里是整型数据)并遍历输出的过程。以下是对这部分代码的简要分析:
头文件引入和命名空间声明:引入了 <iostream>
、<vector>
和 <algorithm>
头文件。使用了 std
命名空间,可以直接使用 std::cout
和 std::endl
等符号,避免了在代码中频繁写 std::
。
定义了自定义函数 myPrint
:函数 myPrint
的作用是输出传入的整数参数,并换行。该函数用于 for_each
算法的回调函数。
test01
函数:在函数 test01
中,首先创建了一个 vector<int>
容器 v
,并向其中插入了四个整数元素(10、20、30、40)。注释掉了使用迭代器进行遍历的代码,通过两种不同的注释掉代码段可以看到迭代器遍历方式的不同,包括 while
和 for
循环遍历方式。使用 for_each
算法对 v
容器中的元素进行遍历,并通过回调函数 myPrint
输出每个元素。
main
函数:在 main
函数中调用了 test01
函数,展示了如何使用 vector
存放数据并进行遍历输出。最后通过 system("pause")
命令使程序在执行完毕后暂停,防止窗口一闪而过。
总体来说,这段代码通过展示了使用 vector
存放整型数据并使用不同的方法进行遍历输出,同时使用 for_each
算法简化了遍历的过程。通过这段代码可以了解如何使用 vector
容器和一些基本的STL算法,提高代码的可读性和简洁性。
#include <iostream>
using namespace std;
#include <vector> // 包含数组容器的头文件
#include <algorithm> // 使用STL中包含的算法时,必须包含这个头文件// vector 存放内置数据类型 和模板那里很像void myPrint(int val)
{cout << val << endl;
}void test01()
{// 创建一个int类型的vector容器,数组vector<int> v;// 向容器中插入数据v.push_back(10);v.push_back(20);v.push_back(30);v.push_back(40);// 通过迭代器访问元素 iterator 迭代器相当于一个指针,因此取出数据时使用解引用方式
// vector<int>::iterator itBegin = v.begin(); // 起始迭代器,指向容器中第一个元素
// vector<int>::iterator itEnd = v.end(); // 结束迭代器,指向容器中最后一个元素的后面一个位置,相当于指向空元素
//
// // 第一种遍历方式
// while (itBegin!=itEnd) // 这里需要加一个判断语句,当元素指向到于itEnd相同时,则不再进行输出
// {
// cout << *itBegin << endl; // 容器相当于一个指针,使用解引用的方式取出数据
// itBegin++; // 指针遍历数组,需要对指针进行++操作,因为数组中一个元素占有4个字节,而指针也是占有4个字节
//
// }// 第二种方式,使用for循环 第一种太过于复杂
// for (vector<int>::iterator it = v.begin(); it != v.end();it++)
// {
// cout << *it << endl;
// }// 第三中使用算法 遍历算法 for_each for_each(v.begin(), v.end(), myPrint); // 算法中传入三个参数 起始迭代器,结束迭代器,函数回调// 这里myPrint()函数传入参数为使用指针的主要原因,是因为,算法内部已经做了解引用的操作,因此返回的就是一个整型数据,并不是地址,// 那么函数中传入参数,也不需要在加指针}int main() {test01();system("pause");return 0;
}
03-Vector存放自定义数据类型
当向
vector
容器中存放自定义的数据类型时,通常需要注意以下几个方面:
定义自定义数据类型:
- 首先需要定义一个自定义的数据类型,可以是类或结构体。这个数据类型可以包含多个不同类型的成员变量,表示自定义的数据结构。
- 在定义类或结构体时,通常需要重载比较运算符或提供自定义的排序规则,以便在
vector
中对自定义数据类型进行排序和查找操作。在
vector
中存储自定义数据类型:
- 创建一个
vector
容器,容器的模板参数为自定义的数据类型。- 使用
push_back()
函数向vector
容器中添加自定义数据类型的对象。遍历和访问:
- 可以通过下标操作或迭代器遍历
vector
中存储的自定义数据类型对象。- 也可以使用算法函数对
vector
中的自定义数据类型对象进行处理,比如排序、查找等操作。
下面是一个简单的示例代码,展示了如何在vector
容器中存储自定义的数据类型(这里以学生类为例)并遍历输出:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;// 自定义学生类
class Student {
public:string name;int age;Student(string n, int a) : name(n), age(a) {}
};// 打印学生信息的函数
void printStudent(const Student &s) {cout << "Name: " << s.name << ", Age: " << s.age << endl;
}int main() {// 创建一个存放学生对象的vectorvector<Student> students;// 向vector中插入学生对象students.push_back(Student("Alice", 20));students.push_back(Student("Bob", 22));students.push_back(Student("Cathy", 21));// 遍历vector并输出学生信息for_each(students.begin(), students.end(), printStudent);return 0;
}
下面给出具体代码分析应用过程:这段代码演示了在vector
容器中存放自定义数据类型(Person
类)的两种方式:一种是存放对象本身,另一种是存放对象的指针。接下来是对这段代码的简要解释:
-
定义自定义数据类型
Person
:Person
类包括m_Name
和m_Age
两个成员变量,分别表示人的姓名和年龄。- 类中定义了构造函数,用来初始化
Person
对象的姓名和年龄。
-
test01
函数:- 首先创建一个
vector<Person>
类型的容器v
。 - 实例化了五个
Person
对象,并通过push_back
方法将它们添加到容器中。 - 使用迭代器遍历容器中的元素,通过
it->m_Name
和it->m_Age
访问每个Person
对象的姓名和年龄进行输出。
- 首先创建一个
-
test02
函数:- 创建了一个
vector<Person*>
类型的容器v
,存放的是Person
对象的指针。 - 将五个
Person
对象的地址添加到容器中。 - 使用迭代器遍历容器中的元素,通过
(*it)->m_Name
和(*it)->m_Age
访问每个Person
对象的姓名和年龄进行输出,这里使用指针的箭头操作符->
来访问指针指向的对象的成员变量。
- 创建了一个
-
main
函数:- 在
main
函数中依次调用了test01
和test02
函数,展示了两种不同存储方式的遍历输出结果。 - 最后通过
system("pause");
命令使程序在执行完毕后暂停,防止窗口一闪而过。
- 在
通过这段代码,展示了如何在vector
容器中存放自定义数据类型对象或指针,并通过迭代器遍历容器中的元素进行访问和输出。同时也展示了对象指针和对象本身在vector
中的存储和访问方式的差异。
#include <iostream>
using namespace std;
#include<vector>
#include<string>// vector存放自定义数据类型 自定义person类型class Person
{public:// 使用构造函数赋初值Person(string name, int age){this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age;
};void test01()
{vector<Person>v;// 创建实例化对象Person p1("A", 10);Person p2("B", 20);Person p3("C", 30);Person p4("D", 40);Person p5("E", 50);// 向容器中插入数据v.push_back(p1);v.push_back(p2);v.push_back(p3);v.push_back(p4);v.push_back(p5);// 遍历容器中元素for (vector<Person>::iterator it = v.begin(); it != v.end();it++)// 这里直接it++{// *it解引用出来的就是Person数据类型,如果需要用的话直接查找类里的成员属性即可
// cout << "姓名:"<<(*it).m_Name <<" 年龄:"<<(*it).m_Age<< endl; // 这里相当于又创建了实例化对象,需要取出成员属性cout << "姓名:" << it->m_Name << " 年龄:" << it->m_Age << endl; // 因为it就是指针,也可以这样使用}}// 存放自定义数据类型 指针void test02()
{vector<Person*>v;// 创建实例化对象Person p1("A", 10);Person p2("B", 20);Person p3("C", 30);Person p4("D", 40);Person p5("E", 50);// 向容器中插入数据v.push_back(&p1);v.push_back(&p2);v.push_back(&p3);v.push_back(&p4);v.push_back(&p5);// 现在存放进去的是一个地址,遍历方式for (vector<Person*>::iterator it = v.begin(); it != v.end();it++){// 上面(*it)返回就是一个Person数据类型,相当于实例化对象,直接.成员属性就行// 而这里(*it)返回的是一个Person*数据类型,(*it)是Person类的指针,使用->取出数据cout << "姓名:" << (*it)->m_Name << " 年龄:" << (*it)->m_Age << endl;}
}int main() {test01();test02();system("pause");return 0;
}
04-Vector容器嵌套容器
当涉及到在
vector
容器中嵌套另一个容器时,通常会有一些需要注意的方面。这种情况下,你可以创建一个容器,该容器的元素是另一个容器,从而实现嵌套的效果。下面是关于在vector
容器中嵌套容器的详细解释:
定义嵌套容器:
- 首先,需要定义内部容器和外部容器的数据类型。内部容器可以是任何STL容器,比如
vector
、list
等。- 外部容器的元素类型是内部容器,因此外部容器的模板参数是内部容器的类型。
操作嵌套容器:
- 创建外部容器时,需要指定内部容器的类型。
- 向外部容器中添加元素时,每个元素都是一个内部容器的实例。
- 可以通过外部容器的迭代器来遍历外部容器,并通过内部容器的迭代器来访问和操作内部容器中的元素。
下面是一个简单的示例代码,演示了如何在vector
容器中嵌套vector
容器,并对嵌套容器进行操作:
在这个示例代码中,nestedVector
是一个vector
容器,其中每个元素都是一个vector<int>
类型的容器。通过将内部vector
容器作为外部vector
容器的元素,实现了嵌套容器的效果。通过两层循环,可以遍历外部容器和内部容器,并访问内部容器中的元素。
嵌套容器提供了一种组织数据的方式,特别适用于需要多维数据结构的场景,如二维矩阵、多级索引等。
#include <iostream>
#include <vector>
using namespace std;int main() {// 定义外部容器,元素类型是内部容器vector<vector<int>> nestedVector;// 添加内部容器到外部容器中nestedVector.push_back(vector<int>{1, 2, 3});nestedVector.push_back(vector<int>{4, 5});nestedVector.push_back(vector<int>{6, 7, 8, 9});// 遍历外部容器并访问内部容器中的元素for (const auto& innerVec : nestedVector) {for (int num : innerVec) {cout << num << " ";}cout << endl;}return 0;
}
下面给出代码详细分析一下应用过程:这段代码演示了在vector
容器中嵌套另一个vector
容器的情况。下面是对代码的简要解释:
-
定义嵌套容器:
- 创建了一个外部
vector<vector<int>>
类型的容器v
,其中每个元素都是一个内部vector<int>
类型的容器。 - 同时,定义了四个内部
vector<int>
类型的容器v1
、v2
、v3
、v4
。
- 创建了一个外部
-
操作内部容器:
- 在内部容器中,使用
push_back
方法向每个容器中插入一些整数数据。
- 在内部容器中,使用
-
将内部容器添加到外部容器:
- 将四个内部容器
v1
、v2
、v3
、v4
添加到外部容器v
中,这样外部容器中就包含了四个内部容器。
- 将四个内部容器
-
遍历嵌套容器:
- 使用外部容器的迭代器遍历外部容器,得到内部容器。
- 对于每个内部容器,再次使用内部容器的迭代器遍历,输出其中的元素。
通过这段代码,展示了如何在vector
容器中嵌套另一个vector
容器,并且使用迭代器对嵌套容器进行遍历和访问。
#include <iostream>
using namespace std;
#include<vector>
#include<string>// 容器嵌套容器void test01()
{vector<vector<int>>v; // 这里就相当于在大容器中,又加入了小容器,为int类型的数组// 在定义小容器vector<int>v1;vector<int>v2;vector<int>v3;vector<int>v4;// 小容器中插入数据 这一步是向小容器中插入数据,for (int i = 0; i < 4;i++){v1.push_back(i + 1);v2.push_back(i + 2);v3.push_back(i + 3);v4.push_back(i + 4);}// 将容器元素插入到大容器中 这一步大容器中也需要有数据v.push_back(v1);v.push_back(v2);v.push_back(v3);v.push_back(v4);// 使用大容器遍历小容器for (vector<vector<int>>::iterator it = v.begin(); it != v.end();it++){// 这个时候可以看出来 *it 返回的是一个vector<int> 也就是有时一个容器,因此还需要一次遍历for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end();vit++){cout << *vit << " ";}cout << endl;}}int main() {test01();system("pause");return 0;
}
实例结果如下图所示:
总结
STL(Standard Template Library,标准模板库)是 C++ 标准库的一部分,提供了一组通用的模板类和函数,用于实现常见的数据结构和算法。以下是关于STL基础知识的总结:
容器(Containers):
- 向量(Vector):动态数组,支持快速随机访问和在末尾插入元素。
- 链表(List):双向链表,支持在任意位置进行插入和删除操作。
- 队列(Queue):先进先出的队列。
- 栈(Stack):后进先出的栈。
- 映射(Map):关联数组,存储键-值对,并根据键快速查找值。
迭代器(Iterators):
- 迭代器用于遍历容器中的元素,提供了统一的访问容器元素的接口。
- 包括输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器。
算法(Algorithms):
- STL提供了各种算法,如排序、查找、变换和合并等。
- 可以通过算法与迭代器结合对容器进行各种操作。
- 一些常见算法包括
sort
、find
、transform
、merge
等。函数对象(Functors):
- 函数对象是重载了
operator()
的类,可以像函数一样调用。- 可以自定义函数对象,并将其用于算法中,比如排序或查找。
适配器(Adapters):
- 堆栈适配器(Stack Adapter):基于
vector
或deque
实现的栈。- 队列适配器(Queue Adapter):基于
deque
或list
实现的队列。分配器(Allocators):
- 分配器用于管理内存分配。
- 可以自定义分配器并用于 STL 容器。
内置类型(Basic Types):
- STL包含了一些内置类型,如
pair
用于存储一对值、tuple
用于存储任意数量的值。总的来说,STL提供了一组灵活、高效且通用的工具,能够帮助我们更方便地实现各种数据结构和算法。熟练掌握STL的基础知识对于C++编程是非常重要的。