该笔记整理(复制)了《C++ Primer Plus第六版》关于STL的部分内容
STL(Standard Template Library)为我们提供了容器、迭代器、函数对象和算法的模板。首先介绍一下基本的概念:
(1)容器(Container):一个类似于数组的单元,可以存储若干个值。STL里的容器是同质的(homogeneous),即存储的值是相同类型的。
(2)迭代器(Iterator):是用于遍历容器的对象,它之于容器恰如指针之于数组,是广义的指针。
(3)函数对象(Function Object):表现得像函数一样的对象,可以是类对象或者函数指针(包括函数名,因为函数名具备像指针一样的作用)。
STL能够帮助我们构造一系列容器,包括向量、队列和链表,并执行很多操作(搜寻、排序和随机排列)。不同于OOP,STL是另一种编程模式——泛型编程(generic programming)。下面进行一些简要记录。
首先通过认识vector来熟悉容器和迭代器。
1.Vector
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> ratings(n);
for (int i = 0; i < n; i++)
cin >> ratings[i];
for (int i = 0; i < n; i++)
cout << ratings[i];
return 0;
}
上面这段简单的代码表现了Vector相比数组的灵活之处,具备动态的分配能力。除去分配存储空间,它还能做什么呢?如下所示:
1)size():返回容器中的元素数目
2)swap():交换两个容器的内容
3)begin():返回一个指向容器里第一个元素的迭代器
4)end():返回一个表示超过容器尾的迭代器
那什么是迭代器?上文提到,它是一个广义指针,实际上,除了作为指针,它还可以是可对其执行类似指针的操作(解除引用、递增)的对象。通过将指针广义化为迭代器,STL能为各种不同的容器类提供统一的接口。每个容器类均定义了一个合适的迭代器,其作用域为整个类。
例如,为vector的double类型规范声明一个迭代器,可以这样做:
vector<double> scores(3);
vector<double>::iterator pd;
pd = scores.begin(); //让pd指向第一个元素
*pd = 22.3; //解引用pd并赋值给socres的第一个元素
++pd; //使pd指向下一个元素
当然,C++11自动类型推断可以方便地这样写:
auto pd = scores.begin();
vector的begin成员函数和end成员函数关系如图所示:
除了上述两种成员函数,push_back(value)也是一个很有用的函数,会将value元素放入原向量的最后一个元素后面。
比如我们创造一个空的向量:
vector<double> scores;
vector<double>::iterator pd;
pd = scores.begin(); //让pd指向第一个元素
scores.push_back(23.5);
for (pd = scores.begin(); pd != scores.end(); pd++)
cout << *pd << endl;
这段代码的运行结果为 “23.5” 。
还有两个成员函数也比较常用,第一个是erase函数:
scores.erase(scores.begin(),scores.begin() + 2);
它的两个参数为it1和it2,作用是删除[it1,it2)之间的元素。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<double> scores = { 1.1,2.2,3.3,4.4,5.5 };
vector<double>::iterator pd;
for (pd = scores.begin(); pd != scores.end(); pd++)
cout << *pd << " ";
cout << endl;
scores.erase(scores.begin() + 1, pd - 1);
for (pd = scores.begin(); pd != scores.end(); pd++)
cout << *pd << " ";
}
输出结果为:
insert作用恰好相反,是在一个区间内插入元素,其共有三个迭代器类型的参数:
其中it1是插入的位置,it2和it3是插入区间,即把向量b里的[it2,it3)插入到向量a里。
例如:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<double> a = { 1, 2, 3, 4 ,5 };
vector<double> b = { 2.1, 2.5, 2.7 };
vector<double>::iterator pd;
cout << "插入前的a:" << endl;
for (pd = a.begin(); pd != a.end(); pd++)
cout << *pd << " ";
cout << endl;
pd = b.end(); //pd指向b里面最后一个元素“2.7”的下一个位置
a.insert(a.begin() + 1, b.begin()+ 1,pd); //插入操作
cout << "插入后的a:" << endl;
for (pd = a.begin(); pd != a.end(); pd++)
cout << *pd << " ";
}
运行结果如下:
2. 对向量可进行的其他操作
除了成员函数,STL还从更广泛的角度定义了非成员函数执行一些操作。即定义了可以适用于所有容器类的非成员函数来执行查找排序等操作。下面简要介绍三个典型的STL函数。
1)for_each(it1,it2,function_ptr):这里面前两个参数是指出容器中区间的迭代器,最后一个是指向函数的指针(更普遍地说最后一个参数是一个函数对象)。该函数将被指向的函数应用于容器区间的各个函数。被指向的函数不可以修改容器元素的值。
vector<Review>::iterator pr;
for (pr = books.begin(); pr != books.end(); pr++)
ShowReview(*pr);
for_each(books.begin(), books.end(), ShowReview);
上面这两段代码的运行效果是一样的。
2)random_shuffle(it1,it2):该函数接受两个指定区间的迭代器参数,并随机排列该区间里的元素。如: random_shuffle(books.begin(), books.end());
与可用于任何容器类的for_each不同,该函数要求容器类允许随机访问,vector类可以做到这一点。
3)sort()函数有两个版本,都非常有用,我们先看版本一:
a) sort(it1,it2): 该版本接受两个定义区间的迭代器参数,并使用为存储在容器里的类型元素定义的<运算符对区间元素进行操作。如下面的语句按照升序对coolstuff里的内容进行排序:
vector<int> coolstuff;
...
sort(coolstuff.begin(), coolstuff.end());
b) sort(it1,it2,function_ptr): 该版本的前两个参数和a里的sort()函数一样,第三个参数则是指向排序方法的函数的指针(函数对象)。排序方法的函数的返回类型应为bool类型,返回false表示不正确的排列。下面举例说明:
struct Review
{
std::string title;
int rating;
};
bool WorseThan(const Review & r1, const Review & r2)
{
if (r1.rating < r2.rating)
return true;
else
return false;
}
vector<Review> books;
sort(books.begin(), books.end(), WorseThan);
最后综合进行一个简单的练习:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class coordinate
{
public:
double x;
double y;
void set_value();
};
void show(const coordinate & p);
bool length(coordinate& p1, coordinate& p2);
int main()
{
cout << "请输入坐标点数目:" << endl;
int n;
cin >> n;
vector<coordinate> a(n);
for (int i = 0; i < n; i++)
{
cout << "输入第" << i+1 << "个坐标: " << endl;
a[i].set_value();
}
for_each(a.begin(), a.end(), show);
sort(a.begin(), a.end(), length);
cout << "按照距离原点排序: " <<endl;
for_each(a.begin(), a.end(), show);
}
void coordinate::set_value()
{
cout << "请输入x: ";
cin >> x;
cout << "请输入y: ";
cin >> y;
cout << "\n";
}
void show(const coordinate & p)
{
cout << "坐标:( " << p.x << "\t," << p.y << " ) " << endl;
}
bool length(coordinate& p1, coordinate& p2)
{
if ((p1.x * p1.x + p1.y * p1.y) <= (p2.x * p2.x + p2.y * p2.y))
return true;
else
return false;
}
这段代码可以实现对坐标点从小到大的排序(基于原点到坐标点的距离)。