前言
最近刚刚开题答辩,现在需要重新投入到战斗中来。
C++标准库是一类强大的数据结构和算法的集合。利用C++泛型的能力,使得一些数据结构和算法得以方便的使用。C++标准库内容很多,我也只打算按照C++primer的顺序,复习其中的IO、容器、泛型算法以及动态内存。这些都是轮子,可以直接拿来用。但是有必要好好阅读和理解其中的源代码,对于我们来说将是很大的提升。
往往在刷题过程中,都需要用到很多数据结构。合理使用它们是一个课题。
本文从IO开始说起,会将大部分有用的知识点写出来。自己能够复习的前提下,把c++基础打扎实。
IO库
IO是计算机一个重要的组成部分。
IO类型
- iostream 从流中读写数据
- fstream 从文件中读写数据
- sstream 从string中读写数据
IO对象无拷贝或赋值。另外读写io对象会改变其状态,因此通过引用传参不能是const的。
顺序容器
顺序容器提供一个顺序访问集合对象的能力,这个顺序不依赖于元素的值,而与元素加入容器的顺序相对应。
顺序容器类型:
- vector:可变大小数组,快速随机访问,在尾部之外插入和删除可能很慢
- deque:双端队列,支持快速随机访问,在头尾插入和删除很快
- list:双向链表,只支持双向顺序访问,在任何位置插入和删除都很快
- forward_list:单向链表,只支持单向顺序访问,在任何位置插入和删除都很快
- array:固定大小数组,支持快速随机访问,不能添加和删除元素
- string:与vector类似的容器,但专门用于保存字符。尾部也可以高效的插入和删除
顺序容易比较常用的操作,我就省略了,这些需要在平时刷题和练习当中使用,才能快速掌握。简单的罗列用处不大。
1.vector是如何增长的
因为vector是很多容器的基础,明白vector的存储策略非常必要。首先,需要明白的是,vector在内存中是连续存储的。这样,当当前无法存储更多的元素时,需要申请新的空间,并且将旧的元素复制到新的存储空间里。为了不频繁的做这样一个操作,容器都会预留一部分作为备用。capacity方法获得就是vector申请空间的大小,而size方法获得是实际存储元素的大小。一般来说,预留空间是空间的一倍。
2.string一些常用的操作
// 数值转换
int i = 42;
string s = to_string(i); // 内置算数类型转string
double d = stod(s); // string转double
int j = stoi(s); // string转int
long long a = stoll(s); // string转long long
float c = stof(s); // string转float
泛型算法
泛型算法独立于容器和类型存在。大多数算法定义在algorithm
头文件里。需要比较清楚的是,这些算法一般不直接操作容器,而是遍历两个头尾迭代器或指针来指定范围。
- 查找
int val = 42;
auto result = find(vec.cbegin(), vec.cend(), val);
cout << resul == vec.cend() ? "is not present" : "is present";
- 累积和
int sum = accumulate(vec.begin(), vec.end(), 0);
- 排序
// 仿函数的写法
struct cmp{
bool operate() (const int &a, const int &b) {
return a < b;
}
}
sort(vec.begin(), vec.end(), cmp());
sort(vec.begin(), vec.end(), less<int>()); // 从小到大排序
sort(vec.begin(), vec.end(), greater<int>()); // 从大到小排序
priority_queue<int, vector<int>, less<int> > p_q; // 最大堆
priority_queue<int, vector<int>, greater<int> > p_q; // 最小堆
- lambda 表达式
lambda 表达式是C++中一种可调用对象,另外还有(函数、函数指针、类)。
表示一个可调用的代码单元(可理解成一个内联函数)。结构是:
[capture list](param list) -> return type {func body};
// 捕获列表表示lambda表达式计算需要使用的函数内的变量
- lambda必须使用尾置返回,默认返回void
- lambda必须要有捕获列表和函数体
- lambda没有默认参数
auto f = [] {return 42;};
cout << f();
stable_sort(vec.begin(), vec.end(),
[](const int &a, const int &b)
{ return a < b;});
编译器在处理lambda表达式时,会生成一个lambda对应的未命名对象。对象中的数据成员通过初始化捕获列表得到。类似于参数传递。
- 值捕获
- 引用捕获
- 隐式捕获(
[=]
或者[&]
)
关联容器
关联容器和顺序容器有很大的不同。关联容器可以说是一种映射,通过关键字来保存和访问元素。
关联容器类型:
1.按关键字有序保存元素, 可遍历
- map : 关联数组,关键字-值对
- set : 关键字即值,只保存关键字
- multimap :关键字可重复的map
- multiset : 关键字可重复的set
2.无序集合, 不可遍历
- unordered_map : 用哈希函数组织的map
- unordered_set : 用哈希函数组织的set
- unordered_multimap : 哈希组织的map,关键字可重复
- unordered_multiset : 哈希组织的set, 关键字可重复
map<int, int> a;
a.find(1) == a.end(); // 找下标
set<int> a;
a.find(1); // 两种都可以,find返回的是迭代器
a.count(1);
// 添加
a.insert({1, 1});
a.insert(1);
// 删除
a.erase(1);
// 注意使用下标访问map时,如果不存在,会添加一个,并初始化
pair类型
pair<string, int> a("1", 2);
make_pair("1", 2);
a = {"1", 2};
a.first;
a.second;
动态内存
C++需要自己控制内存释放,这一点非常重要。因此会写一篇专门讨论C++中的内存申请以及释放的博客,敬请期待。