参考:
C++ STL 教程 | 菜鸟教程 (runoob.com)
C++标准库和标准模板库 - 星朝 - 博客园 (cnblogs.com)
C++强大的功能来源于其丰富的类库及库函数资源。C++标准库的内容总共在50个标准头文件中定义。在C++开发中,要尽可能地利用标准库完成。这样做的直接好处包括:
(1)成本:已经作为标准提供,何苦再花费时间、人力重新开发呢;
(2)质量:标准库的都是经过严格测试的,正确性有保证;
(3)效率:关于人的效率已经体现在成本中了,关于代码的执行效率要相信实现标准库的大牛们的水平;
(4)良好的编程风格:采用行业中普遍的做法进行开发。
在掌握了基本原理的基础上,在认识标准库的问题上完全可以凭借实践,逐步地掌握。标准库的学习不需要认认真真地读书,需要的是在了解概貌的情况下,在实践中深入。
C++标准库
注意,c++标准库并非是glibc,别搞错了。
C++ 标准库包括一组头文件,这些头文件提供了各种功能和工具,涵盖了输入输出、容器、算法、多线程、正则表达式等。
C++ 标准库可以分为两部分:
标准函数库: 这个库是由通用的、独立的、不属于任何类的函数组成的。函数库继承自 C 语言,继承自c语言,但并非和c语言一致。
面向对象类库: 这个库是类及其相关函数的集合。
C++ 标准库包含了所有的 C 标准库,为了支持类型安全,做了一定的添加和修改。
以下是 C++ 标准库的主要组件分类及对应的头文件列表:
输入输出
<iostream>: 标准输入输出流
<fstream>: 文件输入输出流
<sstream>: 字符串流
<iomanip>: 输入输出流格式化
容器
<array>: 定长数组容器
<vector>: 动态数组容器
<deque>: 双端队列容器
<list>: 双向链表容器
<forward_list>: 单向链表容器
<stack>: 栈容器适配器
<queue>: 队列容器适配器
<priority_queue>: 优先队列容器适配器
<set>: 集合容器(基于平衡二叉树)
<unordered_set>: 无序集合容器(基于哈希表)
<map>: 映射容器(键值对,基于平衡二叉树)
<unordered_map>: 无序映射容器(基于哈希表)
<bitset>: 二进制位容器
算法和迭代器
<algorithm>: 常用算法(如排序、查找等)
<iterator>: 迭代器
函数对象和绑定
<functional>: 定义函数对象及相关工具
数学和数值运算
<numeric>: 数值操作(如累计、乘积等)
<complex>: 复数运算
<valarray>: 数组类及相关操作
<cmath>: 数学函数
字符串和正则表达式
<string>: 标准字符串类
<regex>: 正则表达式
时间和日期
<ctime>: 时间处理
<chrono>: 时间库
多线程和并发
<thread>: 多线程支持
<mutex>: 互斥量
<condition_variable>: 条件变量
<future>: 异步编程支持
<atomic>: 原子操作
内存管理
<memory>: 智能指针及动态内存管理
<new>: 动态内存分配
类型特性和运行时类型识别
<type_traits>: 类型特性
<typeinfo>: 运行时类型识别
异常处理
<exception>: 异常处理基类及相关工具
<stdexcept>: 常用异常类(如 std::runtime_error 等)
输入输出操作
<cstdio>: C 风格输入输出
<cstdint>: 定长整数类型
其他工具
<utility>: 通用工具(如 std::pair 和 std::move 等)
<random>
: 随机数生成
<locale>
: 本地化支持
<codecvt>
: 字符编码转换
<cassert>
: 断言
<cctype>
: 字符处理
<cstring>
: 字符串处理
<cwchar>
: 宽字符处理
<climits>
: 数值极限
<cfloat>
: 浮点极限
<cstdlib>
: 常用工具(如std::rand
和std::abs
等)这些头文件构成了C++标准库的基础,提供了丰富的功能,支持开发者进行各种类型的编程任务。
注意,这些头文件都不是.h头文件,而是一个一个的类。
标准模板库STL
STL的代码从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),几乎所有的代码都采用了模板类和模版函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。
C++ 标准模板库(Standard Template Library,STL)是一套功能强大的 C++ 模板类和函数的集合,它提供了一系列通用的、可复用的算法和数据结构。
STL 分为多个组件,包括容器(Containers)、迭代器(Iterators)、算法(Algorithms)、函数对象(Function Objects)和适配器(Adapters)等。
使用 STL 的好处:
代码复用:STL 提供了大量的通用数据结构和算法,可以减少重复编写代码的工作。
性能优化:STL 中的算法和数据结构都经过了优化,以提供最佳的性能。
泛型编程:使用模板,STL 支持泛型编程,使得算法和数据结构可以适用于任何数据类型。
易于维护:STL 的设计使得代码更加模块化,易于阅读和维护。
C++ 标准模板库的核心包括以下重要组件组件:
这些个组件都带有丰富的预定义函数,帮助我们通过简单的方式处理复杂的任务。
对于STL的使用,也普遍存在着两种观点。第一种认为STL的最大作用在于充当经典的数据结构和算法教材,因为它的源代码涉及了许多具体实现方面的问题。第二种则认为STL的初衷乃是为了简化设计,避免重复劳动,提高编程效率,因此应该是“应用至上”的,对于源代码则不必深究。对于初学者而言,通过分析源代码,提高对其应用的理解其意义也不同凡响。
不少C++的经典教材对STL都有非常好的讲解,可以选一本去读。在读书时,要开始学着挑着读,跳着读,不必从头到尾,逐页去读。在这个阶段,可以首先学习迭代器utility、在C++编程中建议替代数组的vector,以及实现双向链表的list。等等。
这里简单介绍一些常用的模板库。
容器
容器其实就是一些数据结构的实现。
关于容器部分,可参考:
C++ 容器类 <vector> | 菜鸟教程 (runoob.com)史上最全的各种C++ STL容器全解析 - Seaway-Fu - 博客园 (cnblogs.com)
vector 容器
参考:C++ 容器类 <vector> | 菜鸟教程 (runoob.com)
C++ 中的 vector 是一种序列容器,它允许你在运行时动态地插入和删除元素。
可以看做一个动态数组。
vector 是基于数组的数据结构,但它可以自动管理内存,这意味着你不需要手动分配和释放内存。
与 C++ 数组相比,vector 具有更多的灵活性和功能,使其成为 C++ 中常用的数据结构之一。
vector 是 C++ 标准模板库(STL)的一部分,提供了灵活的接口和高效的操作。
基本特性:
动态大小:
vector
的大小可以根据需要自动增长和缩小。连续存储:
vector
中的元素在内存中是连续存储的,这使得访问元素非常快速。可迭代:
vector
可以被迭代,你可以使用循环(如for
循环)来访问它的元素。元素类型:
vector
可以存储任何类型的元素,包括内置类型、对象、指针等。使用场景:
当你需要一个可以动态增长和缩小的数组时(类型仍然需要保持一致)。
当你需要频繁地在序列的末尾添加或移除元素时。
当你需要一个可以高效随机访问元素的容器时。
在 C++ 中,使用
<vector>
需要包含头文件<<vector>>
。以下是一些基本的语法:
声明一个
vector
:std::vector<int> myVector;//注意,int是该容器的类型,放在vector类后面的尖括号<>里添加元素:
myVector.push_back(10);访问元素:
int firstElement = myVector[0];获取元素数量:
size_t size = myVector.size();清空
vector
:myVector.clear();下面是一个使用
<vector>
的简单示例,包括输出结果。#include <iostream> #include <vector> int main() { // 声明一个存储整数的 vector std::vector<int> numbers; // 添加元素 numbers.push_back(10); numbers.push_back(20); numbers.push_back(30); // 输出 vector 中的元素 std::cout << "Vector contains: "; for (int i = 0; i < numbers.size(); ++i) { std::cout << numbers[i] << " "; } std::cout << std::endl; // 添加更多元素 numbers.push_back(40); numbers.push_back(50); // 再次输出 vector 中的元素 std::cout << "After adding more elements, vector contains: "; for (int i = 0; i < numbers.size(); ++i) { std::cout << numbers[i] << " "; } std::cout << std::endl; // 访问特定元素 std::cout << "The first element is: " << numbers[0] << std::endl; // 清空 vector numbers.clear(); // 检查 vector 是否为空 if (numbers.empty()) { std::cout << "The vector is now empty." << std::endl; } return 0; }
输出结果:
Vector contains: 10 20 30 After adding more elements, vector contains: 10 20 30 40 50 The first element is: 10 The vector is now empty.
<vector>
是 C++ STL 中一个非常有用的容器,它提供了动态数组的功能,使得元素的添加和删除变得更加灵活和方便。通过上述示例,初学者可以快速了解<vector>
的基本用法和操作。随着学习的深入,你将发现<vector>
在实际编程中的强大功能和广泛应用。
map 容器
参考:C++ 容器类 <map> | 菜鸟教程 (runoob.com)
在 C++ 中,
<map>
是标准模板库(STL)的一部分,它提供了一种关联容器,用于存储键值对(key-value pairs)。
map
容器中的元素是按照键的顺序自动排序的,这使得它非常适合需要快速查找和有序数据的场景。定义和特性
键值对:
map
存储的是键值对,其中每个键都是唯一的。排序:
map
中的元素按照键的顺序自动排序,通常是升序。唯一性:每个键在
map
中只能出现一次。双向迭代器:
map
提供了双向迭代器,可以向前和向后遍历元素。包含头文件:
#include <map>声明 map 容器:
std::map<key_type, value_type> myMap;
key_type
是键的类型。value_type
是值的类型。插入元素:
myMap[key] = value;访问元素:
value = myMap[key];遍历 map:
for (std::map<key_type, value_type>::iterator it = myMap.begin(); it != myMap.end(); ++it) { std::cout << it->first << " => " << it->second << std::endl; }下面是一个使用
map
的简单实例,我们将创建一个map
来存储员工的姓名和他们的年龄,并遍历这个map
来打印每个员工的姓名和年龄。#include <iostream>#include <map> #include <string> int main() { // 创建一个 map 容器,存储员工的姓名和年龄 std::map<std::string, int> employees; // 插入员工信息 employees["Alice"] = 30; employees["Bob"] = 25; employees["Charlie"] = 35; // 遍历 map 并打印员工信息 for (std::map<std::string, int>::iterator it = employees.begin(); it != employees.end(); ++it) { std::cout << it->first << " is " << it->second << " years old." << std::endl; } return 0; }
输出结果:
Alice is 30 years old. Bob is 25 years old. Charlie is 35 years old.map 是 C++ STL 中一个非常有用的容器,特别适合需要快速查找和有序数据的场景。
迭代器
参考:
C++ 标准库 <iterator> | 菜鸟教程 (runoob.com)
C++ 标准库中的
<iterator>
头文件提供了一组工具,用于遍历容器中的元素。迭代器是 C++ 标准模板库(STL)中的核心概念之一,它允许程序员以统一的方式访问容器中的元素,而不需要关心容器的具体实现细节。迭代器是一个对象,它提供了一种方法来遍历容器中的元素。迭代器可以被视为指向容器中元素的指针,但它比指针更加灵活和强大。迭代器可以用于访问、修改容器中的元素,并且可以与 STL 算法一起使用。
迭代器主要分为以下几类:
- 输入迭代器(Input Iterator):只能进行单次读取操作,不能进行写入操作。
- 输出迭代器(Output Iterator):只能进行单次写入操作,不能进行读取操作。
- 正向迭代器(Forward Iterator):可以进行读取和写入操作,并且可以向前移动。
- 双向迭代器(Bidirectional Iterator):除了可以进行正向迭代器的所有操作外,还可以向后移动。
- 随机访问迭代器(Random Access Iterator):除了可以进行双向迭代器的所有操作外,还可以进行随机访问,例如通过下标访问元素。
迭代器的语法通常如下:
#include <iterator> // 使用迭代器遍历容器 for (ContainerType::iterator it = container.begin(); it != container.end(); ++it) { // 访问元素 *it }
下面是一个使用
<iterator>
头文件和迭代器遍历std::vector
的示例:#include <iostream> #include <vector> #include <iterator> int main() { // 创建一个 vector 容器并初始化 std::vector<int> vec = {1, 2, 3, 4, 5}; // 使用迭代器遍历 vector for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) { std::cout << *it << " "; } std::cout << std::endl; // 使用 auto 关键字简化迭代器类型 for (auto it = vec.begin(); it != vec.end(); ++it) { std::cout << *it << " "; } std::cout << std::endl; // 使用 C++11 范围 for 循环 for (int elem : vec) { std::cout << elem << " "; } std::cout << std::endl; return 0; }
输出结果:
1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
通过使用
<iterator>
头文件,我们可以方便地遍历 C++ STL 容器中的元素。迭代器提供了一种统一的接口,使得我们可以在不同的容器之间切换,而不需要改变遍历的代码。这大大提高了代码的可重用性和可维护性。对于初学者来说,理解迭代器的概念和使用方式是非常重要的,因为它们是 C++ STL 的基础。希望这篇文章能帮助你更好地理解迭代器,并在你的 C++ 编程中有效地使用它们。
内存管理库 <memory>
参考:
C++ 内存管理库 <memory> | 菜鸟教程 (runoob.com)
<memory>
是 C++ 标准库中的一个头文件,它包含了用于动态内存管理的模板和函数。在 C++ 中,内存管理是一个重要的概念。动态内存管理允许程序在运行时分配和释放内存,这在处理不确定大小的数据结构时非常有用。然而,不正确的内存管理可能导致内存泄漏、野指针等问题。
<memory>
头文件提供了智能指针等工具,帮助开发者更安全地管理动态内存。智能指针
智能指针是
<memory>
头文件中的核心内容。它们是 C++11 引入的特性,用于自动管理动态分配的内存。智能指针的主要类型有:
std::unique_ptr
:独占所有权的智能指针,同一时间只能有一个unique_ptr
指向特定内存。
std::shared_ptr
:共享所有权的智能指针,多个shared_ptr
可以指向同一内存,内存在最后一个shared_ptr
被销毁时释放。
std::weak_ptr
:弱引用智能指针,用于与shared_ptr
配合使用,避免循环引用导致的内存泄漏。举例:使用
std::unique_ptr
#include <iostream> #include <memory> class MyClass { public: void doSomething() { std::cout << "Doing something" << std::endl; } }; int main() { std::shared_ptr<MyClass> myPtr1(new MyClass()); std::shared_ptr<MyClass> myPtr2 = myPtr1; myPtr1->doSomething(); // 使用 myPtr1 调用成员函数 myPtr2->doSomething(); // 使用 myPtr2 调用成员函数 // 当 myPtr1 和 myPtr2 都被销毁时,MyClass 对象的内存才会被释放 return 0; }
输出结果:
Doing something Doing something其实,就是用来定义一个指针的类型,比如:
std::shared_ptr<MyClass> myPtr2 = myPtr1;
就是定一个指针对象myPtr2,该指针的指针类型是shared_ptr<MyClass>,shared_ptr<MyClass>表示shared_ptr类型是指向MyClass类型。
C++中,用各种类来定义对象,如果有类型,那么类型就需要放在类后面的尖括号<>中。