目录
STL提供了一组表示容器、迭代器、函数对象和算法的模板。
(1.容器是一个与数组类似的单元,来存储数值。
STL容器是同质的,即存储的数值类型相同;
(2.算法是完成特定任务的处方。(如排序查找)
(3.迭代器用来遍历容器的对象,类似于遍历数组的指针,是广义指针。
(4.函数对象是类似于函数的对象,可以是类对象或函数指针(有时函数名被用作指针)。
STL能够构造各种容器(包括数组、队列和链表)和执行各种操作(包括搜索、排序和随机排列)。
STL不是面向对象的编程,而是一种不同的编程模式——泛型编程(generic programming)。
1.模型类vector
本书4.10.1、4.10.2和4.10.3中笔记:
模型类vector类似于string类,也是一种动态数组。
可在运行阶段设置vector对象的长度,可在末尾附加新数据,还可以在中间插入新数据。
它是使用new创建动态数组的替代品。
实际上vector类确实使用new和delete管理内存,但这种工作是自动完成的。
首先,要使用vector对象,必须包含头文件vector。其次,vector 包含在名称空间std中,因此可以使用using编译指令、using声明或std::vector。再次,模板使用不同的语法来指出它存储的数据类类型。最后,vector类使用不同的语法来指定元素数。
示例:
#include<vector>
...
using namespace std;
vector<int>vi;
int n;
cin>>n;
vector<double>vd(n);
其中,vi是vector<int> 对象,vd是一个 vector<double>对象。
由于vector对象在插入或添加时自动调整长度,因此可以将vi的初始长度设置为零。
但要调整长度,需要使用vector包中的各种方法。
一般而言,下面的声明创建了一个名为vt的vector对象,它可以存储n_elem个类型为typeName的元素:
vector<typeName> vt(n_elem);
其中n_elem可以是整型常量,也可以是整形变量。
模型类 array:
vector类的功能比数组强大,但效率稍低。C++11中新增了模板类array,它也位于名称空间std中。
与数组由于,array对象的长度固定的,也是用栈(静态内存分配),而不是自由存储区,因此其效率与数组相同,但更方便与安全。要创建array对象,需要包含头文件array。
array对象的创建语法与vector稍有不同。
#include<array>
...
using namespace std;
array<int,5>ai;
array<double,4>ad;
推而广之,下面的声明创建了一个名为arr的array对象,它包含n_elem个类型为typeName的元素:
array<typeName,n_elem> arr;
无论是数组、vector对象还是array对象,都可使用标准数组表示法来访问各个元素。
其次,从地址可知,array对象和数组存储在相同的内存区域(即栈),而vector对象存储在另一个区域(自由存储区或堆)。
第三,可以将一个array对象赋给另一个array对象;而对于数组必须逐元素复制数据。
此上为笔记。
继续进行16.3.1的笔记
在计算中,矢量(vector)对应数组,而不是第11章介绍的数学矢量。
计算矢量存储了一组可随机访问的值,即用索引直接访问其中的元素。
所以vector类提供了与第14章介绍的valarray和ArrayTP以及第4章介绍的array类似的操作,即可以创建vector对象,对一个vector对象赋给另一个vector对象,使用 [ ] 运算符来访问vector元素。
为了使vector类成为通用的,
STL在vector头文件中定义了一个vector模板。
要创建vector模板对象,可使用通常的<type>表示法来指出使用的类型。
另外vector模板使用动态内存分配,因此可以用初始化参数来指出需要多少矢量。
#include vector
using namespace std;
vector<int> ratings(5); //a vector of 5 ints
int n;
cin>>n;
vector<double>scores(n); //a vector of n doubles
由于 [ ] 运算符被重载,因此创建二次对象后,可以使用通常的数组表示法来访问各个元素:
rating[0]=9;
for(int i=0;i<n;i++)
cout<<scores[i]<<endl;
分配器:
与string类相似,各种STL容器模板都接受一个可选的模板参数,该参数指定使用哪个分配器对象来管理内存。例如,vector模板的开头与下面类似:
template<class T,class Allocator =allocator<T>>
class vector{...
如果省略该模板参数的值,则容器模板将默认使用allocator<T>,这个类使用new和delete。
//vect1.cpp -- introducing the vector template
#include<iostream>
#include<string>
#include<vector>
const int NUM=5;
int main(){
using std::vector;
using std::string;
using std::cin;
using std::cout;
using std::endl;
vector<int> ratings(NUM);
vector<string> titles(NUM);
cout<<"You will do exactly as told.You will enter.\n"
<<NUM<<"book titles and your ratings (0-10).\n";
int i;
for(i=0;i<NUM;i++){
cout<<"Enter titles #"<<i+1<<":";
getline(cin,titles[i]);
cout<<"Enter Your rating (0-10):";
cin>>ratings[i];
cin.get();
}
cout<<"Thank you.You entered the following:\n"
<<"Rating\tBook\n";
for(i=0;i<NUM;i++){
cout<<ratings[i]<<"\t"<<titles[i]<<endl;
}
return 0;
}
此处使用vector模板只是为方便创建动态分配的数组,下一节介绍一个更多类方法的例子。
2. 可对矢量执行的操作
除分配存储空间外,vector模板还能做哪些呢?
所有的STL容器都提供了一些基本方法,包括size()——返回容器中元素数目、swap()——交换两个容器的内容、begin()——返回一个指向容器第一个元素的迭代器、end()——返回一个表示超过容器尾的迭代器。
什么是迭代器?
它是一个广义指针。
实际上,也可以是指针,也可以是一个可对其执行类似指针的操作——如解除引用(如operator*())和递增(如operator++())——的对象。
通过将指针广义化为迭代器,让STL能够为各种不同的容器类(包括那些简单指针无法处理的类)提供统一的接口。
每个容器类都定义了一个合适的迭代器,该迭代器的类型是一个名为iterator的typedef,其作用域为整个类。
例如,要为vector的double类型规范声明一个迭代器,可以这么做:
vector<double>::iterator pd;//pd an iterator
假设scores是一个vector<double>对象:
vector<double> scores;
则可以使用迭代器pd执行这样的操作:
pd=scores.begin(); //have pd point to the first element
*pd=22.3; //dereference pd and assign vallue to first element
++pd; //make pd point to the next element
译:将pd指向第一个元素,解除对pd的引用,并将值赋给第一个元素,将pd指向下一个元素。
可知,迭代器的行为就像指针。
(C++11有自动类型推断:
不是
vector<double>::iterator pd=scores.begin();
而是
auto pd = scores.begin(); //C++11 automatic type deduction
什么是超过结尾(past-the-end)呢?
一种迭代器,指向容器最后一个元素后面的那个元素。(类似于C语言字符串中字符后面的空字符 ‘/0’)只是空字符是一个值,而“超过结尾”是一个指向元素的指针。
end()成员函数标识超过结尾的位置。
如果将迭代器设置为 容器 的第一个元素,并且不断递增,则它最终将到达容器结尾,从而达到遍历整个容器的内容。
如果 scores 和 pd 的定义与前方的示例中相同,则可以用以下代码来展示显示(打印)容器的内容。
for(pd = scores.begin(); pd != scores.end(); pd++)
cout << *pd << endl;
所有容器都包含刚才讨论的那些方法。vector模型类也包含一些只有某些STL容器才有的方法。
push_back()是一个方便的方法,
它将元素添加到矢量末尾。它将负责内存管理,增加矢量的长度,使之能容纳新的成员。即:
vector<double> scores; //create an empty vector
double temp;
while(cin>> temp && temp >= 0)
scores.push-back(temp);
cout<<"You entered "<< scores.size() << "scores.\n";
程序意义为,每次循环都给 scores 对象增加一个元素。
(在编写或运行程序时,无需了解元素的数目。只要有足够的内存,程序就可以根据需要增加scores的长度)
erase()方法删除矢量中给定区间的元素。
他接受两个迭代器参数,这些参数定义了要删除的区间。
第一个迭代器指向区间的起始处,第二个指向区间的终止处的后一个位置。
删除begin()和begin()+1指向的元素:
scores.erase(scores.begin(),socres.begin()+2);
若it1和it2时迭代器,则STL文档使用[p1,p2)来表示从 p1 到 p2 (不包括p2)的区间。因此,区间[begin(),end()]将包括集合的所有内容。(只能出现在文档中)
insert()方法的功能erase()相反。
它接受3个迭代器参数,第一个参数指定了新元素的插入位置,第二个和第三个迭代器参数定义了被插入区间,该区间通常是另一个容器对象的一部分。
将矢量new_v中除第一个元素外的所有元素插入到old_v矢量的第一个元素前面:
vector<int> old_v;
vectoe<int> new_v;
...
old_v.insert(old_v.begin(),new_v.begin()+1,new_v.end());