一、引言
C++ 是一种广泛使用的编程语言,其强大之处不仅在于其高效的性能和灵活的语法,还在于其标准模板库(Standard Template Library,STL)。STL 提供了一系列通用的、高效的算法和数据结构,使得程序员可以更加方便地进行程序开发。本文将详细介绍 C++ 语言的 STL 模板库,包括其各个组件的功能、用法和应用场景。
二、STL 的概述
STL 是 C++ 标准库的一部分,它由容器、算法、迭代器和函数对象等几个主要部分组成。STL 的设计目标是提供一种通用的、高效的编程模式,使得程序员可以更加方便地处理各种数据结构和算法问题。
-
容器
容器是 STL 中用于存储和管理数据的基本数据结构。STL 提供了多种不同类型的容器,包括向量(vector)、链表(list)、队列(queue)、栈(stack)、集合(set)、映射(map)等。每种容器都有其特定的用途和特点,可以根据不同的需求选择合适的容器。
-
算法
算法是 STL 中用于对容器中的数据进行操作的函数模板。STL 提供了大量的算法,包括排序、查找、遍历、复制等。这些算法可以适用于不同类型的容器,使得程序员可以更加方便地对数据进行处理。
-
迭代器
迭代器是 STL 中用于访问容器中元素的一种对象。迭代器提供了一种统一的访问方式,可以遍历容器中的所有元素,而不需要关心容器的具体实现细节。STL 提供了多种不同类型的迭代器,包括输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器等。
-
函数对象
函数对象是 STL 中可以像函数一样调用的对象。函数对象可以用于实现各种算法和操作,例如排序、查找等。STL 中的函数对象可以是普通的函数指针,也可以是类的成员函数指针或者是重载了函数调用运算符(operator ())的类对象。
三、容器
- 向量(vector)
向量是一种动态数组,它可以在运行时动态地调整大小。向量提供了随机访问迭代器,可以快速地访问和修改其中的元素。向量的主要特点包括:- 动态增长:向量可以在运行时根据需要自动增长其大小,以容纳更多的元素。
- 随机访问:向量提供了随机访问迭代器,可以快速地访问和修改其中的元素。
- 高效的内存管理:向量使用连续的内存空间来存储元素,因此可以高效地利用内存。
下面是一个使用向量的示例代码:
收起
cpp
复制
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
for (int i = 0; i < vec.size(); i++) {
std::cout << vec[i] << " ";
}
std::cout << std::endl;
return 0;
}
在上面的代码中,我们创建了一个名为vec
的向量,并向其中添加了三个整数元素。然后,我们使用循环遍历向量中的元素,并输出它们的值。
- 链表(list)
链表是一种线性数据结构,它由一系列节点组成,每个节点包含一个数据元素和一个指向下一个节点的指针。链表的主要特点包括:- 动态增长:链表可以在运行时动态地添加和删除节点,因此可以适应不同大小的数据集合。
- 高效的插入和删除:链表的插入和删除操作只需要修改指针,因此非常高效。
- 非连续的内存布局:链表的节点可以分散在内存中的不同位置,因此不需要连续的内存空间。
下面是一个使用链表的示例代码:
收起
cpp
复制
#include <iostream>
#include <list>
int main() {
std::list<int> lst;
lst.push_back(1);
lst.push_back(2);
lst.push_back(3);
for (std::list<int>::iterator it = lst.begin(); it!= lst.end(); it++) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
在上面的代码中,我们创建了一个名为lst
的链表,并向其中添加了三个整数元素。然后,我们使用迭代器遍历链表中的元素,并输出它们的值。
- 队列(queue)
队列是一种先进先出(FIFO)的数据结构,它支持在队列的末尾插入元素,在队列的开头删除元素。队列的主要特点包括:- 先进先出:队列中的元素按照插入的顺序依次被取出,先插入的元素先被取出。
- 高效的插入和删除:队列的插入和删除操作只需要在队列的末尾或开头进行,因此非常高效。
- 有限的容量:队列通常具有有限的容量,当队列已满时,插入操作会失败。
下面是一个使用队列的示例代码:
收起
cpp
复制
#include <iostream>
#include <queue>
int main() {
std::queue<int> que;
que.push(1);
que.push(2);
que.push(3);
while (!que.empty()) {
std::cout << que.front() << " ";
que.pop();
}
std::cout << std::endl;
return 0;
}
在上面的代码中,我们创建了一个名为que
的队列,并向其中添加了三个整数元素。然后,我们使用循环遍历队列中的元素,并输出它们的值。在每次输出元素之前,我们使用que.front()
获取队列的头部元素,然后使用que.pop()
删除队列的头部元素。
- 栈(stack)
栈是一种后进先出(LIFO)的数据结构,它支持在栈的顶部插入元素,在栈的顶部删除元素。栈的主要特点包括:- 后进先出:栈中的元素按照插入的顺序依次被取出,后插入的元素先被取出。
- 高效的插入和删除:栈的插入和删除操作只需要在栈的顶部进行,因此非常高效。
- 有限的容量:栈通常具有有限的容量,当栈已满时,插入操作会失败。
下面是一个使用栈的示例代码:
收起
cpp
复制
#include <iostream>
#include <stack>
int main() {
std::stack<int> stk;
stk.push(1);
stk.push(2);
stk.push(3);
while (!stk.empty()) {
std::cout << stk.top() << " ";
stk.pop();
}
std::cout << std::endl;
return 0;
}
在上面的代码中,我们创建了一个名为stk
的栈,并向其中添加了三个整数元素。然后,我们使用循环遍历栈中的元素,并输出它们的值。在每次输出元素之前,我们使用stk.top()
获取栈的顶部元素,然后使用stk.pop()
删除栈的顶部元素。
- 集合(set)
集合是一种无序的、不重复的数据结构,它支持快速的插入、删除和查找操作。集合的主要特点包括:- 无序性:集合中的元素没有特定的顺序。
- 唯一性:集合中的元素是唯一的,不允许重复。
- 高效的查找:集合支持快速的查找操作,可以在常数时间内确定一个元素是否在集合中。
下面是一个使用集合的示例代码:
收起
cpp
复制
#include <iostream>
#include <set>
int main() {
std::set<int> st;
st.insert(1);
st.insert(2);
st.insert(3);
st.insert(2);
for (std::set<int>::iterator it = st.begin(); it!= st.end(); it++) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
在上面的代码中,我们创建了一个名为st
的集合,并向其中添加了三个整数元素。注意,当我们尝试向集合中添加重复的元素时,集合会自动忽略重复的元素。然后,我们使用迭代器遍历集合中的元素,并输出它们的值。
- 映射(map)
映射是一种关联容器,它将一个键值对(key-value pair)映射到一个特定的值。映射的主要特点包括:- 键值对:映射中的每个元素都是一个键值对,其中键是唯一的,用于标识元素,值可以是任意类型的数据。
- 有序性:映射中的元素按照键的顺序进行排序。
- 高效的查找:映射支持快速的查找操作,可以在常数时间内根据键找到对应的值。
下面是一个使用映射的示例代码:
收起
cpp
复制
#include <iostream>
#include <map>
int main() {
std::map<std::string, int> mp;
mp["apple"] = 1;
mp["banana"] = 2;
mp["orange"] = 3;
for (std::map<std::string, int>::iterator it = mp.begin(); it!= mp.end(); it++) {
std::cout << it->first << ": " << it->second << std::endl;
}
return 0;
}
在上面的代码中,我们创建了一个名为mp
的映射,并向其中添加了三个键值对。然后,我们使用迭代器遍历映射中的元素,并输出它们的键和值。
四、算法
- 排序算法
STL 提供了多种排序算法,包括快速排序、归并排序、堆排序等。这些算法可以对不同类型的容器进行排序,并且可以根据不同的需求选择合适的排序算法。
下面是一个使用快速排序算法对向量进行排序的示例代码:
收起
cpp
复制
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
std::sort(vec.begin(), vec.end());
for (int i = 0; i < vec.size(); i++) {
std::cout << vec[i] << " ";
}
std::cout << std::endl;
return 0;
}
在上面的代码中,我们使用std::sort()
函数对一个向量进行排序。std::sort()
函数接受两个迭代器作为参数,表示要排序的范围。在这个例子中,我们对整个向量进行排序。
- 查找算法
STL 提供了多种查找算法,包括线性查找、二分查找等。这些算法可以在不同类型的容器中查找特定的元素,并且可以根据不同的需求选择合适的查找算法。
下面是一个使用二分查找算法在向量中查找特定元素的示例代码:
收起
cpp
复制
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int target = 7;
std::vector<int>::iterator it = std::lower_bound(vec.begin(), vec.end(), target);
if (it!= vec.end() && *it == target) {
std::cout << "Found " << target << " at index " << std::distance(vec.begin(), it) << std::endl;
} else {
std::cout << target << " not found" << std::endl;
}
return 0;
}
在上面的代码中,我们使用std::lower_bound()
函数在一个有序的向量中查找特定的元素。std::lower_bound()
函数接受三个参数,分别是要查找的范围的起始迭代器、结束迭代器和要查找的元素。如果找到了要查找的元素,std::lower_bound()
函数会返回一个迭代器,指向第一个大于或等于要查找的元素的位置。如果没有找到要查找的元素,std::lower_bound()
函数会返回结束迭代器。
- 遍历算法
STL 提供了多种遍历算法,包括for_each()
、transform()
等。这些算法可以对不同类型的容器进行遍历,并对容器中的元素进行各种操作。
下面是一个使用for_each()
算法遍历向量并输出每个元素的示例代码:
收起
cpp
复制
#include <iostream>
#include <vector>
#include <algorithm>
void printElement(int element) {
std::cout << element << " ";
}
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::for_each(vec.begin(), vec.end(), printElement);
std::cout << std::endl;
return 0;
}
在上面的代码中,我们定义了一个名为printElement()
的函数,它接受一个整数参数,并输出这个整数。然后,我们使用std::for_each()
函数遍历一个向量,并将每个元素作为参数传递给printElement()
函数。std::for_each()
函数接受三个参数,分别是要遍历的范围的起始迭代器、结束迭代器和一个函数对象。在这个例子中,我们将printElement()
函数作为函数对象传递给std::for_each()
函数。
- 复制算法
STL 提供了多种复制算法,包括copy()
、copy_backward()
等。这些算法可以将一个容器中的元素复制到另一个容器中。
下面是一个使用copy()
算法将一个向量中的元素复制到另一个向量中的示例代码:
收起
cpp
复制
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> src = {1, 2, 3, 4, 5};
std::vector<int> dst(src.size());
std::copy(src.begin(), src.end(), dst.begin());
for (int i = 0; i < dst.size(); i++) {
std::cout << dst[i] << " ";
}
std::cout << std::endl;
return 0;
}
在上面的代码中,我们首先创建了一个名为src
的向量,并向其中添加了五个整数元素。然后,我们创建了一个名为dst
的向量,其大小与src
向量相同。接着,我们使用std::copy()
函数将src
向量中的元素复制到dst
向量中。std::copy()
函数接受三个参数,分别是要复制的范围的起始迭代器、结束迭代器和目标容器的起始迭代器。在这个例子中,我们将src
向量的起始迭代器和结束迭代器作为前两个参数,将dst
向量的起始迭代器作为第三个参数。
五、迭代器
-
迭代器的类型
STL 提供了多种不同类型的迭代器,包括输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器等。每种迭代器都有其特定的用途和特点,可以根据不同的需求选择合适的迭代器。
- 输入迭代器:输入迭代器只能用于读取容器中的元素,不能用于修改容器中的元素。输入迭代器只能向前移动,不能向后移动。
- 输出迭代器:输出迭代器只能用于向容器中写入元素,不能用于读取容器中的元素。输出迭代器只能向前移动,不能向后移动。
- 前向迭代器:前向迭代器可以用于读取和修改容器中的元素,并且可以向前移动。前向迭代器是输入迭代器和输出迭代器的超集。
- 双向迭代器:双向迭代器可以用于读取和修改容器中的元素,并且可以向前和向后移动。双向迭代器是前向迭代器的超集。
- 随机访问迭代器:随机访问迭代器可以用于读取和修改容器中的元素,并且可以像指针一样进行随机访问。随机访问迭代器是双向迭代器的超集。
-
迭代器的操作
迭代器提供了一系列操作,用于访问和修改容器中的元素。这些操作包括解引用(dereference)、递增(increment)、递减(decrement)、比较(comparison)等。
- 解引用:解引用操作符(*)用于获取迭代器所指向的元素。解引用操作符返回一个引用,可以用于读取或修改元素的值。
- 递增:递增操作符(++)用于将迭代器向前移动一个位置。递增操作符可以用于遍历容器中的元素。
- 递减:递减操作符(--)用于将迭代器向后移动一个位置。递减操作符可以用于反向遍历容器中的元素。
- 比较:比较操作符(==、!=、<、<=、>、>=)用于比较两个迭代器是否相等或具有特定的顺序关系。比较操作符可以用于判断迭代器是否到达容器的末尾或确定两个迭代器之间的相对位置。
-
迭代器的适配器
STL 还提供了一些迭代器适配器,用于将一种迭代器转换为另一种迭代器。迭代器适配器包括反向迭代器(reverse iterator)、插入迭代器(insert iterator)和流迭代器(stream iterator)等。
- 反向迭代器:反向迭代器用于反向遍历容器中的元素。反向迭代器可以将双向迭代器或随机访问迭代器转换为反向迭代器,使得可以从容器的末尾开始向前遍历元素。
- 插入迭代器:插入迭代器用于向容器中插入元素。插入迭代器可以将输出迭代器转换为插入迭代器,使得可以向容器中插入元素而不是覆盖已有元素。
- 流迭代器:
再详细一些 代码要小学生都能理解的那种 变量名用字母
《C++ 语言 STL 模板库详解》
一、引言
C++ 是一种功能强大的编程语言,而标准模板库(Standard Template Library,STL)则是 C++ 的重要组成部分。STL 提供了一系列通用的数据结构和算法,使得程序员可以更加高效地编写程序。本文将以简单易懂的方式介绍 C++ 语言的 STL 模板库,帮助大家更好地理解和使用它。
二、STL 的概述
STL 主要由容器、算法和迭代器三部分组成。容器用于存储数据,算法用于对容器中的数据进行操作,迭代器则用于在容器中遍历数据。
三、容器
- 向量(vector)
向量就像是一个可以自动变大的盒子,你可以往里面放东西,也可以从里面拿东西。
收起
cpp
复制
#include <iostream>
#include <vector>
int main() {
// 创建一个名为 v 的向量,可以放整数
std::vector<int> v;
// 往向量里放数字 1
v.push_back(1);
// 再放数字 2
v.push_back(2);
// 又放数字 3
v.push_back(3);
// 看看向量里有几个数字
std::cout << "向量里有 " << v.size() << " 个数字。" << std::endl;
// 从向量里拿出第一个数字并打印
std::cout << "第一个数字是:" << v[0] << std::endl;
// 从向量里拿出第二个数字并打印
std::cout << "第二个数字是:" << v[1] << std::endl;
// 从向量里拿出第三个数字并打印
std::cout << "第三个数字是:" << v[2] << std::endl;
return 0;
}
- 链表(list)
链表就像一串珠子,每个珠子都有一个数字,而且珠子之间可以连接起来。
收起
cpp
复制
#include <iostream>
#include <list>
int main() {
// 创建一个名为 l 的链表,可以放整数
std::list<int> l;
// 在链表末尾加上数字 1
l.push_back(1);
// 再在末尾加上数字 2
l.push_back(2);
// 又在末尾加上数字 3
l.push_back(3);
// 看看链表有多长
std::cout << "链表的长度是 " << l.size() << std::endl;
// 从链表的开头开始,一个一个地拿出数字并打印
for (std::list<int>::iterator it = l.begin(); it!= l.end(); it++) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
- 队列(queue)
队列就像排队一样,先到的人先出去。
收起
cpp
复制
#include <iostream>
#include <queue>
int main() {
// 创建一个名为 q 的队列,可以放整数
std::queue<int> q;
// 把数字 1 放进队列
q.push(1);
// 再把数字 2 放进队列
q.push(2);
// 又把数字 3 放进队列
q.push(3);
// 看看队列里有几个数字
std::cout << "队列里有 " << q.size() << " 个数字。" << std::endl;
// 从队列里拿出第一个数字并打印
std::cout << "第一个数字是:" << q.front() << std::endl;
// 把第一个数字从队列里拿走
q.pop();
// 再看看第一个数字是多少
std::cout << "现在第一个数字是:" << q.front() << std::endl;
return 0;
}
- 栈(stack)
栈就像一摞盘子,最后放上去的盘子最先被拿走。
收起
cpp
复制
#include <iostream>
#include <stack>
int main() {
// 创建一个名为 s 的栈,可以放整数
std::stack<int> s;
// 把数字 1 放进栈
s.push(1);
// 再把数字 2 放进栈
s.push(2);
// 又把数字 3 放进栈
s.push(3);
// 看看栈里有几个数字
std::cout << "栈里有 " << s.size() << " 个数字。" << std::endl;
// 拿出栈顶的数字并打印
std::cout << "栈顶的数字是:" << s.top() << std::endl;
// 把栈顶的数字拿走
s.pop();
// 再看看栈顶的数字是多少
std::cout << "现在栈顶的数字是:" << s.top() << std::endl;
return 0;
}
- 集合(set)
集合就像一个装着不同东西的盒子,里面的东西不会重复。
收起
cpp
复制
#include <iostream>
#include <set>
int main() {
// 创建一个名为 st 的集合,可以放整数
std::set<int> st;
// 把数字 1 放进集合
st.insert(1);
// 再放数字 2
st.insert(2);
// 又放数字 1(因为集合里不能有重复的数字,所以这个 1 不会被放进去)
st.insert(1);
// 看看集合里有几个数字
std::cout << "集合里有 " << st.size() << " 个数字。" << std::endl;
// 从集合里拿出一个数字并打印
for (std::set<int>::iterator it = st.begin(); it!= st.end(); it++) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
- 映射(map)
映射就像一本字典,你可以通过一个词找到它的意思。
收起
cpp
复制
#include <iostream>
#include <map>
int main() {
// 创建一个名为 mp 的映射,可以把一个字符串和一个整数对应起来
std::map<std::string, int> mp;
// 把字符串"apple"和数字 1 对应起来放进映射
mp["apple"] = 1;
// 把字符串"banana"和数字 2 对应起来放进映射
mp["banana"] = 2;
// 把字符串"orange"和数字 3 对应起来放进映射
mp["orange"] = 3;
// 看看映射里有几个键值对
std::cout << "映射里有 " << mp.size() << " 个键值对。" << std::endl;
// 通过字符串"apple"找到对应的数字并打印
std::cout << "apple 对应的数字是:" << mp["apple"] << std::endl;
return 0;
}
四、算法
- 排序算法
排序算法可以把一组数字从小到大或者从大到小排列好。
收起
cpp
复制
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
// 创建一个名为 v 的向量,里面有一些数字
std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
// 用排序算法把向量里的数字从小到大排列
std::sort(v.begin(), v.end());
// 打印排好序的数字
for (int i = 0; i < v.size(); i++) {
std::cout << v[i] << " ";
}
std::cout << std::endl;
return 0;
}
- 查找算法
查找算法可以在一组数字里找到你想要的那个数字。
收起
cpp
复制
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
// 创建一个名为 v 的向量,里面有一些数字
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 要找的数字
int target = 7;
// 用查找算法找这个数字
std::vector<int>::iterator it = std::find(v.begin(), v.end(), target);
// 如果找到了这个数字
if (it!= v.end()) {
std::cout << "找到了数字 " << target << "。" << std::endl;
} else {
std::cout << "没有找到数字 " << target << "。" << std::endl;
}
return 0;
}
- 遍历算法
遍历算法可以一个一个地看一组数字里的每个数字。
收起
cpp
复制
#include <iostream>
#include <vector>
#include <algorithm>
void printNumber(int number) {
std::cout << number << " ";
}
int main() {
// 创建一个名为 v 的向量,里面有一些数字
std::vector<int> v = {1, 2, 3, 4, 5};
// 用遍历算法一个一个地打印向量里的数字
std::for_each(v.begin(), v.end(), printNumber);
std::cout << std::endl;
return 0;
}
- 复制算法
复制算法可以把一组数字复制到另一个地方。
收起
cpp
复制
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
// 创建一个名为 src 的向量,里面有一些数字
std::vector<int> src = {1, 2, 3, 4, 5};
// 创建一个名为 dst 的向量,大小和 src 一样
std::vector<int> dst(src.size());
// 用复制算法把 src 里的数字复制到 dst 里
std::copy(src.begin(), src.end(), dst.begin());
// 打印 dst 里的数字
for (int i = 0; i < dst.size(); i++) {
std::cout << dst[i] << " ";
}
std::cout << std::endl;
return 0;
}
五、迭代器
-
迭代器的类型
- 输入迭代器:就像一个只能看不能改的指针,只能向前走。
- 输出迭代器:就像一个只能写不能读的指针,只能向前走。
- 前向迭代器:可以读也可以改,只能向前走。
- 双向迭代器:可以读也可以改,可以向前走也可以向后走。
- 随机访问迭代器:可以像数组下标一样随机访问,可以读也可以改,可以向前走也可以向后走。
-
迭代器的操作
- 解引用(*):就像用指针指向一个数字,然后可以看到这个数字的值。
- 递增(++):就像把指针向前移动一个位置。
- 递减(--):就像把指针向后移动一个位置。
- 比较(==、!=、<、<=、>、>=):就像比较两个指针的位置关系。
-
迭代器的适配器
- 反向迭代器:可以从容器的末尾开始向前遍历。
- 插入迭代器:可以向容器中插入元素。
- 流迭代器:可以从输入流中读取元素或者向输出流中写入元素。
六、总结
STL 模板库是 C++ 语言中非常强大的工具,它提供了丰富的数据结构和算法,可以大大提高程序员的开发效率。通过本文的介绍,希望大家对 STL 模板库有了更深入的了解,并且能够在实际的编程中灵活运用它.