作为C++标准库相当重要的一部分,STL库提供一系列组件操作。它主要可以分为容器、迭代器、基本算法、函数对象以及内存分配器和配接器六个部分。整个STL库的代码都采用模板函数以及模板类的方式实现,具有高度的通用性。对于传统的应用程序来讲,模板库支持并且倡导一种新的编程风格,即称为泛型编程思想,以通用的模板方式来编写应用程序中的数据结构与算法。
16.1 STL常见容器
C++标准STL库中封装实现了常见数据结构,并以容器的方式提供给用户使用。STL容器主要包含vector向量、deque队列、list链表、set集合与map映射等。每个类别容器相应通过一个模板类对外提供操作接口。通过提供的容器类类型,开发者可以自定义封装实现相应的数据结构类的对象。通过定义时传入真正的数据类型来实例化产生对应容器类的对象实例,并且在外部通过这些类提供的方法接口来实现应用程序中需求处理功能。
16.2.1 STL容器介绍
STL模板库容器大致可以分为两类:一类是以线性组织方式存储类型相同的对象的序列式容器,另一类是使用树结构的关联式容器。
1.序列式容器
序列式容器在STL中主要包含常见的三种:向量vector、链表list和双端队列deque。
q 向量vector为一种顺序存储同类型元素的数据结构。它是一种数组方式的思路实现,并且可以随机访问的序列。
q 链表list是一种实现双向链表数据结构的容器。它只支持顺序访问序列中的元素。该容器在删除、插入相应元素时,效率较高。另外,list可以与vector一样在需要时动态改变其大小。
q 双端队列deque是类似向量的一种队列结构。它允许在该队列的头部和尾部插入并且删除相应的元素。同时,它也支持针对其元素的顺序以及随机访问。
2.关联式容器
关联式容器则是采用了key-value键值的方式存储和访问容器中的元素。关联式容器采用树结构组织数据,支持快速、随机并且高效的检索其中的元素。关联式容器主要包含set、multiset、map以及multimap容器。
q set容器支持随机存取,并且其键与对应的数据元素为同一个值。该容器中所有的元素必须是唯一的,不能包含重复的元素。
q multiset容器则在set基础上可以包含重复的元素。同样,该容器的键与对应的元素也是为同一值。
q map是一个包含键值对的容器。该结构中存放的键为索引使用的关键字,而对应的值则为真正存放的数据。该容器允许存在重复的键值,但每个键只能与一个值相互对应。
q multimap提供单个关键词可以对应多个数据的数据结构操作,供实际应用中选择使用。
顺序式容器与关联式容器在底层实现,通过不同的数据结构类型来区分。针对提供的不同的操作接口,用户并不需要了解具体容器底层实现,可以直接通过对外公布的接口访问对应的数据元素。而容器的设计实现则是主要基于数组、链表以及二叉树基本数据结构原型的底层实现。开发者通常只需要了解容器的基本功能以及应用场合,就能够在实际应用中选择合适的容器来处理相应的数据。
16.2.2 vector向量
vector向量容器的数据结构的原型为数组类型。只不过该容器通过封装实现vector模板类实现动态数组结构,并提供基于该顺序存储结构的操作接口供开发者调用。vector容器在使用时,必须包含相应的头文件(#include<vector>)。头文件提供了相应的操作接口声明。一旦包含之后,就可以定义vector对象实例存储并操作相应类型的数据元素。
由于vector为模板类,所以在实际定义vector对象时,需要指定该模板类型参数。而相应的模板参数可以是内置数据类型,也可以是自定义的结构体、类等类型。例如,以下代码根据传入的实际类型来确定该vector实例化存储相应的类型元素。
#include<vector>
std::vector;
vector<string>stringValue; //第一个定义string标准字符串类型的vector对象stringValue
vector<int> intValue; //第二个定义int整型类型的vector对象intValue
vector<float> floatValue(5,6.0); //第三个定义float浮点类型的vector对象floatValue
vector<MyString>MyStringValue(5); //第四个定义自定义字符串操作MyString类型的vector对象MyStringValue
上述代码中,标准的库都采用std名字空间,使用相应的标准库的中类型操作。一个应用程序需要使用标准名字空间内的API,需要先声明在该名称空间下。std::vector表明使用的是std名字空间下的该类。通常情况下,建议使用全局方式定义std名字空间。即使用using namespace std;语句来公开表示以下应用程序中使用的标准库操作都来自于该名字空间。
实例中主要根据具体的参数类型,采用vector类提供的构造函数来构造实例化对象。根据vector类提供的对外构造函数接口,可以采用不同的构造函数来构造该容器类对象实例。
q 第一个stringValue对象为定义string类型的空vector。该容器是个模板实现,其中存放的元素必须为相应传入模板实例化的参数类型,即上述语句主要定义空的可以存放string类型元素的vector向量。
q 第二个则同样定义一个空vector对象,该对象中存放着整型元素。
q 第三个实例对象的定义是根据vector类提供的另一个构造函数接口而来。该构造函数结构接口对象定义语法为vector<Type> objectName(size,value),即定义vector对象实例同时指定初始化元素的大小以及对应存放的值。实例三中实际定义了vector对象floatValue,该向量存放float浮点型数据元素,并且在构造时指定其大小为5,同时每个元素都采用数据值6.0来初始化。
q 第四个实例中则直接通过自定义字符串类型MyString作为实例化参数类型,定义指定大小为5个元素的向量MyStringValue。
16.2.3 vector基本操作演示
vector向量在模板库中既然以类方式实现,那么除了提供对应的构造函数用于定义具体vector对象实例外,还提供了相当丰富的对外操作vector向量的公开接口方法。这些方法主要用于数据元素的操作,如表16.1所示。
表16.1 向量vector接口方法说明
接口方法名称 |
基本功能说明 |
vectorObject.assign(start,end) |
将迭代器start与end之间的数据元素赋值给vectorObject |
vectorObject.assign(n, elem) |
将n个elem元素赋给其调用的vectorObject |
vectorObject.at(index) |
返回向量vectorObject中指定索引index所指的数据元素 |
vectorObject.back() |
返回向量vectorObject中最后一个数据元素 |
vectorObject.begin() |
返回指向向量vectorObject中第一个元素的迭代器 |
vectorObject.capacity() |
返回向量vectorObject中元素的个数 |
vectorObject.clear() |
清空向量vectorObject中的数据元素 |
vectorObject.empty() |
判断向量vectorObject是否为空,为空则返回true值 |
vectorObject.end() |
返回指向向量vectorObject最后一个数据元素的迭代器 |
vectorObject.erase(loc) |
删除当前向量loc位置数据元素,返回下一个数据元素位置 |
vectorObject.erase(start,end) |
删除从start到end区间的元素,包含start不包含end,并返回下一个数据元素位置 |
vectorObject.front() |
返回向量中第一个数据元素 |
vectorObject.insert(pos,elem) |
当前向量中pos位置插入元素elem,并返回新数据的位置 |
vectorObject.insert(pos,n,elem) |
当前向量中pos位置插入n个elem数据元素,不返回任何值 |
vectorObject.insert(pos,start,end) |
当前向量中pos位置处插入区间在start到end之间的数据元素,包含start但不包含end,无任何返回值 |
vectorObject.max_size() |
返回当前向量中最大数据量,即向量的最大长度 |
vectorObject.pop_back() |
删除当前向量最后一个数据元素 |
vectorObject.push_back(elem) |
当前向量尾部添加一个数据元素 |
vectorObject.rbegin() |
返回一个逆向队列的第一个数据元素 |
vectorObject.rend() |
返回一个逆向队列的最后一个数据元素 |
VectorObject.reserve() |
设置当前向量合适的最小容量 |
vectorObject.resize(num) |
重新设定当前向量的长度 |
vectorObject.size() |
返回当前容器中实际数据元素个数 |
v1.swap(v2) |
呼唤向量v1与v2的数据元素 |
operator[] |
重载下标操作符,用于访问指定向量中的元素 |
下面将会通过一个完整的实例,调用vector相应的接口,操作vector存储的数据。代码编辑如下所示。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1601.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1601
* 源文件chapter1601.cpp
* vector基本操作实例
*/
#include <iostream>
#include <vector>
using namespace std;
int main()
{
//第一部分代码
vector<int> intValue; //定义int整型类型的vector对象intValue
vector<int>::iterator iter; //定义int整型类型的迭代器对象iter
vector<int>::iterator tempIter; //定义int整型类型的迭代器对象tempiter
for(int i = 0;i < 10;i++) //定义for循环控制结构,从变量i值为0开始,循环10次
{
intValue.push_back(i); //控制结构内实现将i变量递增值逐个放入容器intValue中
}
cout<<"intValue size:"<<intValue.size()<<endl; //通过vector提供的size()方法计算该容器内元素个数
//第二部分代码
cout<<"intValueelement:";
for(int i = 0;i < intValue.size();i++) //for循环内,通过vector的at方法逐个访问输出元素
{
cout<<intValue.at(i)<<" ";
}
cout<<endl;
//第三部分代码
intValue.assign(5,6); //通过vector的assign方法来实现容器内元素值的替换
cout<<"intValue element:";
for(int i = 0;i < intValue.size();i++) //通过for循环控制结果循环打印输出容器内元素值
{
cout<<intValue.at(i)<<" ";
}
cout<<endl;
//第四部分代码
iter = intValue.begin(); //通过begin()方法将迭代器指向容器intValue第一个元素
intValue.insert(iter,100); //通过vector的insert方法将迭代器指向的元素处插入值100
intValue.insert(iter,3,200); //通过vector的insert方法将迭代器指向的元素处插入3 //个200的值
cout<<"intValue element:";
for(iter = intValue.begin();iter != intValue.end();iter++) //循环控制输出容器元素值
{
cout<<*iter<<" ";
}
cout<<endl;
//第五部分代码
cout<<"intValue element:"<<endl; //提示下面将会进行一些操作后打印输出容器内容
int size = intValue.size(); //通过vector提供的size()方法获取到该容器内长度
for(int i = 0;i < size;i++) //通过for循环遍历该容器元素
{
iter = intValue.begin(); //首先将迭代器指向容器intValue第一个元素
intValue.erase(iter); //通过vector提供的erase方法删除指向的第一个元素
for(tempIter = intValue.begin();tempIter !=intValue.end();tempIter++)//通过//临时的迭代器访问输出此 //时容器内的元素值
{
cout<<*tempIter<<"";
}
cout<<endl;
}
intValue.clear(); //通过vector提供的clear()方法清空容器intValue内元素
if(intValue.empty()) //通过if结构,结合vector提供的empty()方法判断容器是否为空
{
cout<<"The vector intValue isempty!"<<endl; //为空则输出提示信息
}
else
{
cout<<"haveelements!"<<endl; //不为空则输出提示信息
}
return 0;
}
程序中主要使用了vector容器的插入、删除数据操作。具体程序运行讲解见后面程序剖析。
2.编辑makefile
Linux平台下需要编译的源文件为chapter1601.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1601.o
CC=g++
chapter1601: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1601
clean:
rm-f chapter1601 core $(OBJECTS)
submit:
cp-f -r chapter1601 ../bin
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该程序,运行结果如下所示。
[developer@localhost src]$ make
g++ -c-o chapter1601.o chapter1601.cpp
g++ chapter1601.o -g -o chapter1601
[developer @localhost src]$ make submit
cp -f -r chapter1601 ../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1601
intValue size:10
intValue element:0 1 2 3 4 5 6 7 8 9
intValue element:6 6 6 6 6
intValue element:200 200 200 100 6 6 6 6 6
intValue element:
200 200 100 6 6 6 6 6
200 100 6 6 6 6 6
100 6 6 6 6 6
6 6 6 6 6
6 6 6 6
6 6 6
6 6
6
The vector intValue is empty!
4.剖析程序
上述实例主要演示了标准模板库vector容器提供的基本操作应用。应用程序中首先定义一个整型参数类型的空向量vector,该向量实例化为处理整型元素的容器。随后定义两个作用于vector相对应的迭代器,迭代器iter、tempIter作用于向量intValue上,类似指针,通常可以用于遍历处理容器中的数据元素。下面将程序分为五个部分来进行剖析讲解。
第一个部分应用实例中使用的第一个方法接口使为push_back(elem)。该方法主要用于向空向量的尾部添加数据元素。程序中采用for循环控制结构,循环地在向量intValue中放入10个元素。其存放的为循环递增的i值。空向量intValue中存放10个元素后,使用该对象调用其方法成员size求取该向量中元素个数,并在屏幕上打印输出。从程序运行结果可以看出,该向量中包含10个数据元素。
第二个部分主要演示向量容器at方法的使用。根据for循环控制结构,递增式访问向量容器中的数据元素。以当前向量长度size返回值为for循环判断值,依次递增变量i。通过传入实参i值调用at方法,返回对应位置的数据元素并打印输出。此时,该容器中存放了刚刚放入的递增值,位置从0~9,共10个元素。
第三个主要演示方法接口assign的使用。根据前面的接口说明,传入两实参分别为5和6。5表示需要替换的元素的个数,6表示相应的值。但是需要注意的是,该替换是整个所有元素的替换,即将当前向量中的元素全部采用5个6来替换,其余元素都不复存在了。随后的at()方法循环遍历打印元素说明了这点。替换后的向量中只剩下了5个元素值为6的数据。
第四个主要演示了insert方法接口(在指定位置插入元素)的使用,首先将迭代器iter指向当前vector的首元素,随后使用当前向量对象调用insert方法,根据迭代器iter所指的位置插入实参100。由于当前迭代器指向位置为向量的首元素,所以在向量中首元素前插入数据100。此时,向量中存放的数据即为(100,6,6,6,6,6)。第二个insert方法接口的调用则采用重载实现的另外一个插入操作接口。该接口主要根据迭代器所指的位置,插入指定数目的指定元素。应用程序中主要在向量的首部插入3个元素为200的数据,此时向量中的数据为(200,200,200,100,6,6,6,6,6)。随后的部分通过for循环控制,通过cout流对象输出代码中采用迭代器遍历向量方法打印输出并验证了该向量中此时的数据元素。
第五个演示了erase方法接口(删除向量中指定位置)的使用。通过两个for循环控制结构,依次递增的删除向量中元素的操作。根据向量中的数据长度首先采用erase方法调用删除向量第一个元素,随后第二重for循环则打印输出剩下的向量数据元素。随后调用了clear方法清空了当前向量,然后根据empty方法成员调用判断,当前向量是否为空,由于前面已经将该向量清空,此时empty方法返回true,随后输出打印对应的提示信息。
vector向量提供了众多的操作方法接口,本书由于篇幅限制无法一一演示使用方法。初学者可以根据上述演示的过程,通过标准库提供的接口基本说明,在应用程序中尝试着使用这些接口。
16.2.4 deque队列
deque容器为双端可操作队列,非常类似于向量vector数据结构。仅仅是双端队列可以在容器的两端插入以及删除对应的数据元素,并且提供的操作效率较高。双端队列deque同样也提供了随机访问功能,用户也可以根据需要动态地改变队列的大小。
应用程序中要使用双端队列deque容器,同样需要包含相应的头文件(#include<deque>)。同样模板类deque提供了多个构造函数重载实现形式,允许根据需要,定义不同的双端队列的对象实例。下面通过几个实例定义,帮助读者了解deque对象构造情况。
#include <deque> //队列包含的头文件
std::deque; //队列名字空间声明
deque<int> intValue; //定义整型类型队列对象intValue
deque<string>stringValue(100); //定义string标准字符串类型队列对象stringValue
deque<double>doubleValue(10,200.0); //定义double类型队列对象doubleValue
deque<float>floatValue1(floatValue2); //定义float类型队列对象floatValue1
deque<MyString> MyStringValue(first,last); //定义自定义MyString类型队列对象MyStringValue
第一个实例定义中通过整型参数实例化队列对象,定义了双端队列对象实例intValue。此时双端队列为空,可以存储整型类型的数据元素。
第二个实例定义中则采用string字符串类型实例化deque容器。通过在定义时传入实参来指定容器的大小,这里指定该容器大小为100。
第三个实例中使用deque容器构造原型(deque<type> dequename(size,elem))构造双端队列对象实例。其中,size为容器的长度,elem则为所有元素赋初始值。这里,定义了double型双端队列容器doubleValue,容量大小为10,每个元素初始化为200.0。
第四个对象实例构造则是采用内部拷贝构造的方式,将根据现有的deque队列对象实例创建一个新的对象实例。实例中以float类型实例化队列,并以现有的同类型floatValue2对象拷贝构造实现。
最后一个实例定义则以自定义字符串类型实例化deque,定义对象实例以迭代器first到last区间创建有限制范围的双端队列MyStringValue。
16.2.5 deque基本操作演示
deque队列容器与向量容器实现结构类似,甚至在提供的接口方法上也基本相同,仅仅有一小部分的区别。读者可以通过表16.2,了解双端队列deque对外公开提供的操作接口基本情况,以便在应用程序中正确地操作使用。
表格16.2 双端队列deuqe公开方法接口说明
接口方法名称 |
基本功能说明 |
d.assign(n,elem) |
n个elem元素取代当前队列容器中元素 |
d.assign(first,end) |
迭代器first到end区间的元素取代当前队列中元素 |
d.at(n) |
访问当前队列n位置处的元素,并返回该元素 |
d.back() |
返回当前队列尾部元素 |
d.begin() |
返回当前队列第一个元素的迭代器 |
d.clear() |
清空当前队列中所有元素 |
d.empty() |
判断当前队列是否为空,如果为空则返回true |
d.end() |
返回当前队列中最后一个元素的迭代器 |
d.erase(first,end) |
删除当前队列迭代器first到end所指区间的元数据 |
d.erase(iter) |
删除当前队列迭代器iter所指位置元素 |
d.front() |
返回当前队列起始位置元素 |
d.insert(iter,elem) |
当前队列迭代器iter位置处插入元素elem |
d.insert(iter,first,end) |
将迭代器first到end区间的元素插入到当前队列iter所指位置 |
d.insert(iter,num,elem) |
将num个elem元素插入到当前队列iter所指位置处 |
d.max_size() |
返回当前队列容器当前最大容量 |
d.pop_back() |
删除当前队列中最后一个元素 |
d.pop_front() |
删除当前队列中第一个元素 |
d.push_back(elem) |
当前队列的尾部添加元素elem |
d.push_front(elem) |
当前队列的头部添加元素elem |
d.rbegin() |
返回当前队列反向的指向首元素的迭代器 |
d.resize(num,elem) |
将当前队列大小调整为num,并且使用元素elem初始化容器 |
d.size() |
返回当前队列的元素个数 |
d.swap(deque) |
交换当前队列与deque队列的内容 |
下面通过一个完整的双端队列接口操作的实例,帮助读者了解双端队列deque接口使用的基本情况。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1602.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1602
* 源文件chapter1602.cpp
* deque容器基本操作实例
*/
#include <iostream>
#include <deque>
using namespace std;
int main()
{
//第一部分代码
deque<char> charValue; //定义char类型队列对象charValue
deque<char> charValue1; //定义char类型队列对象charValue1
deque<char>::iterator iter; //定义char类型队列迭代器iter
deque<char>::iterator tempIter; //定义char类型队列迭代器tempIter
for(int i = 0;i < 10;i++) //for循环从变量i值为0循环10次
{
charValue.push_front(i+65); //通过队列提供push_front方法从队列前面插入元素,值为i+65
charValue1.push_front(i+65); //通过队列提供push_front方法从队列前面插入元素,值为i+65
}
cout<<"charValue elements:"; //打印输出队列中元素值
for(int i = 0;i < charValue.size();i++) //通过for循环,遍历队列长度
{
cout<<charValue.at(i)<<" "; //通过队列的at()方法输出内部元素值
}
cout<<endl;
//第二部分代码
charValue.assign(4,‘A‘); //通过队列assign方法来替换内部元素,采用4个A替换所有元素
cout<<"charValue elements:";
for(int i = 0;i < charValue.size();i++) //打印输出替换后的队列中元素值
{
cout<<charValue[i]<<" ";
}
cout<<endl;
//第三部分代码
iter = charValue.begin(); //通过begin()方法将迭代器iter指向队列容器charValue首个元素
charValue.insert(iter,‘B‘); //通过insert()方法将字符B插入iter指向的位置
charValue.insert(iter,4,‘C‘); //通过insert()方法将4个字符C插入iter指向的位置
cout<<"charValue elements:"; //打印输出插入操作后的队列容器中的元素值
for(iter = charValue.begin();iter != charValue.end();iter++)
{
cout<<*iter<<" ";
}
cout<<endl;
//第四部分代码
cout<<"charValue elements:"<<endl; //提示下面将会通过删除操作pop_back操作容器元素
int size = charValue.size(); //通过队列提供size方法获取队列长度
for(int i = 0;i < size;i++) //循环操作遍历队列元素
{
charValue.pop_back(); //通过队列提供的pop_back方法从尾部删除元素
for(tempIter = charValue.begin();tempIter !=charValue.end();tempIter++)//循环遍历该容器内元素
{
cout<<*tempIter<<"";
}
cout<<endl;
}
//第五部分代码
charValue.swap(charValue1); //通过队列提供的swap方法交换不同的队列
cout<<"charValue elements:";
for(iter = charValue.begin();iter != charValue.end();iter++) //交换后打印输出队列charValue的元素值
{
cout<<*iter<<" ";
}
cout<<endl;
charValue.clear(); //通过队列容器提供的clear方法清空其中的元素
if(charValue.empty()) //通过队列提供的empty判断容器是否为空
{
cout<<"The deque charValue isempty!"<<endl; //为空输出提示
}
else
{
cout<<"haveelements!"<<endl; //不为空输出提示
}
return 0;
}
本实例主函数中演示deque常见操作的使用,如基本的插入、删除数据操作。具体程序运行讲解见后面程序剖析。
2.编辑makefile
Linux平台下需要编译的源文件为chapter1602.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1602.o
CC=g++
chapter1602: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1602
clean:
rm-f chapter1602 core $(OBJECTS)
submit:
cp-f -r chapter1602 ../bin
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c-o chapter1602.o chapter1602.cpp
g++ chapter1602.o -g -o chapter1602
[developer @localhost src]$ make submit
cp -f -r chapter1602../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1602
charValue elements:J I H G F E D C B A
charValue elements:A A A A
charValue elements:B C C C C A A A A
charValue elements:
B C C C C A A A
B C C C C A A
B C C C C A
B C C C C
B C C C
B C C
B C
B
charValue elements:J I H G F E D C B A
The deque charValue is empty!
4.程序剖析
本实例程序主要演示了deque队列操作,通过该类容器提供的各类方法,实现对容器中各元素操作。主程序中首先定义两个字符类型实例化的空双端队列容器分别为charValue与charValue1,同时定义两个同类型的迭代器iter与tempIter,便于后续操作中使用。下面将会将程序分为五个部分分别剖析讲解。
第一个部分操作实例则是使用push_front方法,在双端队列的头部添加数据元素。此时采用for循环控制结构,两个deque对象都调用相应的push_front方法,把传入的变量i+65作为实参添加元素。由于双端队列中存放着字符型元素,所以此时传入的i+65被内部转化为字符存储。按照应用程序设定,for循环*把10个元素存放于相应的队列中。随后使用了deque类中at()方法,根据传入的相应元素的位置索引,在for循环中打印输出队列charValue中的元素。由于是从头部开始添加元素,因此最终打印输出的结果为从字符J~A,共10个元素。
第二个部分操作实例中则主要演示了assign方法用于替换双端队列charValue中的元素。根据调用时传入的实参,采用4个‘A‘字符替换当前队列中的数据元素。随后采用队列方法中下标操作符,根据传入的下标变量遍历打印输出替换后的数据元素,此时双端队列中存放的即为4个字符‘A‘。
第三个部分实例操作主要演示了双端队列中插入元素,通过deque提供的重载实现的insert方法来实现不同的插入操作功能。首先将预先定义的迭代器iter指向当前队列charValue的首元素,随后调用insert(iter,elem)方法,传入实参为当前队列的首元素迭代器,插入的元素数据为字符‘B‘。而接下来调用的则是insert方法的重载接口,在指定的位置插入指定个数的元素数据。实例中在队列的首部插入4个字符‘C‘。随后采用迭代器遍历的方式打印输出双端队列charValue中的元素,此时元素数据即为插入相应数据之后的队列。
第四个部分应用操作实例演示删除元素操作功能,与向量实例中算法思路相同,采用了两个for循环。每次删除一个元素后,都会遍历打印输出队列中所剩下的元素。第一重循环中首先会使用pop_back方法从队列的尾部删除元素,每次删除一个元素,并且随后打印输出整个队列中的元素情况。上述实例的程序运行结果已经很好的体现了这部分的操作应用。
第五个部分应用操作则是演示双端队列中的swap方法,该方法主要用于交换不同的双端队列。当前的队列charValue调用swap方法,该方法传入的实参为另一个队列,此时charValue1队列中仍然存放的元素J~A。该方法最终采用charValue1队列替换charvalue,所以此时采用迭代器遍历输出该队列时,就为J~A的元素。
最后应用实例中当前队列charValue调用了clear方法,清空了该队列中所有元素,随后if判断结构中使用empty方法判断该队列。如果该队列为空,则返回true值,随后打印输出相应提示信息。
16.2.6 list列表
STL标准库中list容器实际上是封装实现了双向链表数据结构,该容器非常适合做频繁的插入与删除操作。相对于向量vector来讲,list的优势就在于此,但是list容器采用下标操作速度相对不理想。由于是双向链表结构,list容器提供双向的迭代器用于遍历操作容器中数据,同样支持动态扩充改变容器本身的大小,而不需要额外的操作。
同样应用程序中要使用list容器时,需要包含相应的list的头文件(#include<list>)。链表容器模板类提供了几类构造函数方法接口,下面将会根据提供的list容器构造函数方法讲述其对象构造情况。
#include <list>
list<int> intValue; //定义int整型类型列表对象intValue
list<string>stringValue(10); //定义string标准字符串列表对象stringValue
list<double>doubleValue(10,1.0); //定义double类型列表对象doubleValue
list<int> intValue1(intValue); //定义整型类型列表对象intValue1
list<int> intValue2(firstIter,endIter); //定义整型类型列表对象intValue2
上述实例中一共定义了5个list容器对象实例,分别对应着list容器提供的5个构造函数接口,根据不同的需要,定义相对应的对象实例。
q 第一个对象实例依然是定义一个空整型实例的list容器对象intValue。使用该对象实例时调用构造函数中默认无参数的接口。
q 第二个对象实例定义则使用list提供的带有初始化大小的构造函数list(num)。在构造其对象时,根据传入的实参10,来初始化当前stringValue链表的大小。
q 第三个构造函数使用在具体参数类型实例化list对象时,除了指定list大小以外,同时还指定相应的数据元素来初始化对应的链表中数据。上述实例中定义一个包含10个元素的double型参数链表,同时指定使用1.0数值来初始化对应的元素。
q 第四个实例构造依然采用拷贝构造的方式。定义整型list容器intValue1时,根据传入的实参intValue容器直接拷贝生成该对象。此时,intValue1对象实例的情况与拷贝的list容器对象相同。
q 最后一个构造定义是采用迭代器在构造对象同时指定对应着迭代器范围的元素的链表对象。此时list对象中包含迭代器从firstIter到endIter之间的数据元素。
16.2.7 list基本操作演示
序列型的容器提供的接口操作大致相同,仅仅在个别操作方面有一些差别。与前面两个容器介绍一样,下面将会通过相应的表格列出list容器大部分操作接口以及操作基本说明,随后通过完整实例来演示其中部分方法,帮助读者了解序列容器基本操作方法的使用情况。表格16.3列出了大部分list容器的接口方法,如下所示。
表格16.3 list容器公开接口方法说明
接口方法名称 |
基本功能说明 |
assign(n,value) |
用n个元素值value替换当前链表中元素 |
assign(firstIter,endIter) |
用迭代器firstIter到endIter区间内的元素替换链表中元素 |
back() |
返回当前链表最后一个元素的引用 |
begin() |
返回指向当前链表首个元素的迭代器 |
clear() |
清空当前链表中所有元素 |
empty() |
判断当前链表中元素是否为空,为空则返回true |
end() |
返回指向当前链表最后一个元素的迭代器 |
erase(iter) |
删除当前链表中指示器iter指向的元素 |
erase(firstIter,endIter) |
删除当前链表迭代器firstIter到endIter区间内的元素 |
front() |
返回当前链表第一个元素的引用 |
insert(iter,value) |
当前链表中迭代器iter所指位置插入元素value |
insert(iter,num,value) |
当前链表中迭代器iter所指位置插入num个元素value |
insert(iter,firstIter,endIter) |
当前链表中迭代器iter所指位置插入区间firstIter到endIter之间的元素 |
max_size() |
返回当前链表最大容量,即当前链表可存放元素最大个数 |
pop_back() |
删除当前链表中最后元素 |
pop_front() |
删除当前链表中首元素 |
push_back(value) |
当前链表尾部添加元素value |
push_front(value) |
当前链表首部添加元素value |
rbegin() |
返回当前链表反向的首元素迭代器 |
remove(value) |
删除当前链表中所有值为value的元素 |
删除容器指定元素 |
|
rend() |
放回指向当前反向链表最后一个元素的指示器 |
resize(num,value) |
调整当前链表,使得当前链表为num个元素,并且将元素初始化为value值 |
reverse() |
反调当前链表中元素的顺序 |
size() |
返回当前链表中元素个数 |
sort() |
根据默认排序方式,对当前的链表中元素排序 |
sort(method) |
根据当前给定的模式,对当前链表中元素排序 |
swap(listValue) |
当前链表与listValue链表内部元素互换 |
unique() |
删除当前链表中重复元素 |
链表list容器提供了如上一大堆方法接口,大部分与其它序列容器相仿,调用操作方法大致也相同。下面将会通过一个完整实例演示list容器基本接口方法操作应用,实例代码编辑如下。
1.准备实例
打开UE工具,创建新的空文件并另存为chapter1603.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1603
* 源文件chapter1603.cpp
* list容器基本操作实例
*/
#include <iostream>
#include <list>
using namespace std;
int main()
{
//第一部分代码
list<int> intValue1; //定义int整型类型的list对象intValue1
list<int> intValue2; //定义int整型类型的list对象intValue1
list<int>::iterator iter; //定义整型list的迭代器iter
for(int i = 0;i < 10;i++) //循环控制,整型变量i从值0到10实现10次循环
{
intValue1.push_back(i); //通过list的push_back()方法将i值从尾部放入到list容器中
intValue2.push_front(i); //通过list的push_front()方法将i值从头部放入到list容器中
}
cout<<"List intValue1 elements:"; //提示输出list容器内元素值
for(iter = intValue1.begin();iter != intValue1.end();iter++) //通过for循环遍历list容器元素
{
cout<<*iter<<" "; //输出迭代器指向的list容器里元素的值
}
cout<<endl;
//第二部分代码
iter = intValue1.begin(); //通过begin()方法将容器intValue1首元素赋值给迭代器iter指向
intValue1.insert(iter,3,10); //通过insert()方法将容器intValue1内iter指向的位置插入3个值为10 //的元素
cout<<"List intValue1 elements:"; //提示遍历容器intValue1内的元素
for(iter = intValue1.begin();iter != intValue1.end();iter++) //通过for循环遍历容器intValue1元素
{
cout<<*iter<<" "; //输出迭代器iter指向的元素值
}
cout<<endl;
//第三部分代码
intValue1.remove(10); //通过容器提供的remove()方法删除容器intValue1中值为10的元素
iter = intValue1.begin(); //通过begin()方法将容器intValue1第一个元素赋值给iter指向
intValue1.erase(iter); //通过erase()方法删除iter指向的元素
cout<<"List intValue1 elements:"; //提示输出容器元素
for(iter = intValue1.begin();iter != intValue1.end();iter++) //遍历容器元素
{
cout<<*iter<<" "; //输出迭代器iter指向元素值
}
cout<<endl;
//第四部分代码
intValue1.reverse(); //通过reverse()方法实现容器内元素值反序排列
cout<<"List intValue1 elements:"; //提示输出容器元素
for(iter = intValue1.begin();iter != intValue1.end();iter++) //循环遍历容器元素
{
cout<<*iter<<" "; //输出iter指向值
}
cout<<endl;
intValue1.merge(intValue2); //通过merge()方法合并两个列表容器的元素
cout<<"List intValue1 elements:"; //提述输出容器元素
for(iter = intValue1.begin();iter != intValue1.end();iter++) //循环遍历容器元素
{
cout<<*iter<<" "; //输出iter指向值
}
cout<<endl;
return 0;
}
本实例主要在主函数中实现list常见基本操作使用情况,程序中主要使用了list容器中基本的插入、删除数据操作。具体程序运行讲解见后面程序剖析。
2.编辑makefile
Linux平台下需要编译的源文件为chapter1603.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1603.o
CC=g++
chapter1603: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1603
clean:
rm-f chapter1603 core $(OBJECTS)
submit:
cp-f -r chapter1603 ../bin
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c-o chapter1603.o chapter1603.cpp
g++ chapter1603.o -g -o chapter1603
[developer @localhost src]$ make submit
cp -f -r chapter1603../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1603
List intValue1 elements:0 1 2 3 4 5 6 7 8 9
List intValue1 elements:10 10 10 0 1 2 3 4 5 67 8 9
List intValue1 elements:1 2 3 4 5 6 7 8 9
List intValue1 elements:9 8 7 6 5 4 3 2 1
List intValue1 elements:9 8 7 6 5 4 3 2 1 9 8 76 5 4 3 2 1 0
4.剖析程序
上述实例主要演了list容器中几个不同于前面的序列容器的方法接口。下面将程序分为四个部分来进行剖析讲解。
第一个部分代码在主程序中首先定义两个空的整型实例化链表容器对象intValue1与intvalue2。随后使用一个for循环控制结构初始化这两个整型list容器的元素。intValue1采用push_back(i)方法循环的从链表尾部添加元素,元素随着i变量的递增而递增。此时打印输出intValue1容器中的元素为“0 1 2 3 4 5 6 7 8 9”。
第二部分代码应用实例中采用insert方法,在当前链表中指定位置插入相应的元素。首先将list容器迭代器指向当前链表intValue1首元素;随后调用insert方法,根据传入的实参在iter迭代器指向的位置插入3个值为10的元素。此时intValue1链表中元素打印输出结果为“10 10 0 1 2 3 4 5 6 7 89”。
第三个部分代码采用remove与erase方法根据传入的实参,删除相应元素。remove方法根据传入的实参数值,删除对应所有元素值与传入的实参值相同的元素。而erase方法则根据实参迭代器指向的位置,删除该迭代器指向的元素。此时打印输出intValue1容器中的内容为“1 2 3 4 5 6 7 8 9”。首先remove(10)删除了当前链表中所有值为10的元素,随后迭代器iter指向当前链表首部元素,将该迭代器作为实参,删除该迭代器指向的元素,即值为0的元素被删除。
第四个部分代码则是反转链表方法,reverse方法用于将当前链表中元素全部按照反顺序排列。反转后的当前链表intValue1的元素为“9 8 7 6 5 4 3 2 1”。最后一个则演示了merge合并链表接口操作,当前链表内容为“9 8 7 6 5 4 3 2 1”,需要合并的链表容器为intValue2,该容器中的元素同样为“9 8 7 6 5 4 3 2 1”,因为之前采用的push_front方法从链表首部添加元素。合并之后的链表则将传入的链表内容放入当前链表的前部,此时intValue1中元素为“9 8 7 6 5 4 3 2 1 9 8 7 6 5 4 3 2 1”。
16.2.8 set集合
set集合容器是采用二叉树为基础的数据结构实现。由于删除和插入元素操作都只要修改指针的指向,因此set集合在插入与删除元素操作上效率非常高。set是一种允许随机存取元素的容器,同样是键与值关系的容器,但是要求set集合容器中的元素值必须唯一,不能重复。
关联容器set依然是模板类实现。定义对象实例时,根据传入的类型实例化具体的集合。该容器同样提供了几种构造对象的实现方式,用于不同的情况下构造集合对象实例。示例如下。
#include <set>
std::set; //声明set集合名字空间
set<char> charValue1; //定义char字符类型的集合对象charValue1
set<char> charValue2(charValue1); //定义char字符类型的集合对象charValue2
set<char>charValue3(firstIter,endIter); //定义char字符类型的集合对象charValue3
同样,在使用set容器时需要包含其头文件(#include<set>)。另外,STL模板库是封装在std名称空间之下的,所以外部使用时需要外部声明名字空间。上述代码中,根据set容器提供的构造函数,定义三个不同的对象实例。
q 第一个为字符型实例化的空集合set对象实例charValue1,此时该容器实例中无任何数据。
q 第二个则根据set容器提供的拷贝构造函数,将第一个定义的对象实例拷贝生成当前set集合容器的实例对象charValue2。该集合对象实例与实例charValue1完全一样。
q 最后一个则根据当前迭代器firstIter到endIter之间的集合来构造初始化集合对象实例charValue3。此时该对象中包含有上述迭代器区间的集合元素。
16.2.9 set基本操作演示
关联容器中也提供了大量针对集合容器通用操作接口。与前面介绍的任何一种容器类似,这些方法接口实现结合容器上某种功能。根据需要,开发者可以在实际应用中根据集合容器对象实例来直接调用。set容器大部分方法接口如表16.4所示。
表格16.4 set容器公开接口方法说明
接口方法名称 |
基本功能说明 |
begin() |
返回指向当前集合容器的第一个元素的迭代器 |
clear() |
清空当前集合容器 |
count(value) |
统计并返回当前集合容器中元素value出现的次数 |
empty() |
判断当前集合容器是否为空,为空则返回true |
end() |
返回指向当前集合容器的最后一个元素的迭代器 |
equal_range(value) |
返回当前集合容器中元素为value的第一个位置以及最后一个位置的迭代器 |
erase(iter) |
删除当前集合容器中迭代器iter指向的元素 |
erase(value) |
删除当前集合容器中值为value的元素 |
erase(firstIter,endIter) |
删除当前集合容器中区间为firstIter到endIter迭代器之间的元素 |
find(value) |
返回当前集合容器中指向元素value的迭代器,如果没有找到则返回该集合最后一个元素的迭代器与end方法同 |
insert(value) |
当前集合容器中插入元素value,按照默认方式排序元素 |
insert(iter,value) |
当前集合容器中iter迭代器指向位置前插入元素value,并且返回一个指向该元素的迭代器 |
insert(firstIter,endIter) |
将迭代器firstIter到endIter区间的元素插入到当前集合容器中 |
lower_bound(value) |
如果value元素在当前集合中则返回当前元素指向的迭代器,如果value元素不在集合中则返回指向下一个元素的迭代器 |
upper_bound(value) |
如果value元素在当前集合中则返回下一个元素指向的迭代器,如果不存在也返回下一个元素指向的迭代器 |
max_size() |
返回当前集合容器的最大容量 |
rebegin() |
返回当前反向集合容器中第一个元素的迭代器 |
rend() |
返回当前反向集合容器中最后一个元素的迭代器 |
size() |
返回当前集合中实际元素个数 |
swap(setcontains) |
交换两个集合容器内容 |
set集合容器操作接口大部分与序列容器相同,下面将会通过完整操作set集合容器的实例,演示提供的不同的方法接口的应用情况,实例代码编辑如下所示。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1604.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1604
* 源文件chapter1604.cpp
* set基本操作实例
*/
#include <iostream>
#include <set>
using namespace std;
int main()
{
//第一部分代码
set<char> charValue; //定义字符类型集合对象charValue
set<char>::iterator iter; //定义字符类型集合迭代器iter
for(int i = 0;i < 10;i++) //for循环从变量i为0开始循环10次
{
charValue.insert(65+i); //通过集合的insert方法向集合插入元素,元素值为65+i,其中i为变量
}
cout<<"set elements:"; //提述循环遍历容器中的元素
for(iter = charValue.begin();iter != charValue.end();iter++) //通过for循环,通过迭代器遍历容器charValue
{
cout<<*iter<<" "; //打印迭代器指向元素的值
}
cout<<endl;
//第二部分代码
iter = charValue.begin(); //通过begin()方法迭代器iter指向容器charValue的首元素
charValue.erase(++iter); //通过erase()方法删除迭代器指向的下一个元素的值
cout<<"set elements:"; //打印输出此时的集合内容
for(iter = charValue.begin();iter != charValue.end();iter++) //同样采用迭代器遍历容器charValue
{
cout<<*iter<<" "; //输出迭代器指向的元素值
}
cout<<endl;
//第三部分代码
int count; //定义整型变量count
count = charValue.count(‘B‘); //通过集合的count方法计算容器中元素为B字符的个数赋值给count变量
cout<<"B counts:"<<count<<endl; //打印输出其中指定字符B的元素个数
//第四部分代码
iter = charValue.find(‘B‘); //通过find()方法找出元素B的位置,将结果赋值给迭代器iter
if(iter == charValue.end()) //如果遍历值容器尾部还未找到该元素B,则打印提示信息
{
cout<<"not found thiselement!"<<endl;
}
else
cout<<"found thiselement:"<<*iter<<endl; //否则提示找到
//第五部分代码
iter = charValue.lower_bound(‘E‘); //通过lower_bound()方法返回元素E指定的位置,赋值给迭代器iter
cout<<"current*iter:"<<*iter<<endl; //输出迭代器指向的元素值
charValue.clear(); //通过clear()方法清空容器charValue
if(charValue.empty()) //通过empty()方法判断容器是否为空
{
cout<<"set contains isempty!"<<endl; //为空输出提示信息
}
else
cout<<"not empty!"<<endl; //不为空,输出提示信息
return 0;
}
本实例主要在主函数中实现set常见基本操作使用情况,程序中主要使用了set容器中基本的插入、删除数据操作。具体程序运行讲解见后面程序剖析
2.编辑makefile
Linux平台下需要编译的源文件为chapter1604.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1604.o
CC=g++
chapter1604: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1604
clean:
rm-f chapter1604 core $(OBJECTS)
submit:
cp-f -r chapter1604 ../bin
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c-o chapter1604.o chapter1604.cpp
g++ chapter1604.o -g -o chapter1604
[developer @localhost src]$ make submit
cp -f -r chapter1604../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1604
set elements:A B C D E F G H I J
set elements:A C D E F G H I J
B counts:0
not found this element!
current *iter:E
set contains is empty!
4.程序剖析
上述实例中主要演示了部分set集合容器的方法接口操作。
第一个部分定义字符型实例化的set集合空容器charValue,同时定义该类型容器的迭代器iter。set容器并没有提供类似序列容器的push操作,容器对象创建后,根据for循环控制结构采用insert方法循环插入10个元素。该元素值为65+i内部转化为相应的字符。set集合容器根据插入的元素会按照默认的方式来排序当前的数据元素,本实例中是按照ASCII码值从小到大插入数据的。默认情况下,即使不是按照这个顺序插入数据,内部也会采用默认的方式来排序容器中的元素。
set集合容器对象创建后,通过迭代器遍历该容器,并打印输出其中的元素。10个元素从A开始根据i变量的递增分别插入元素为A~J字符。
第二部分代码操作演示通过erase方法删除集合容器中迭代器指定位置的元素。首先使迭代器iter定位指向容器的首部位置,调用erase方法内部iter优先执行了++操作运算,将迭代器指向下一个元素,所以此时删除集合中第二个元素,即元素‘B‘。通过迭代器遍历方式打印输出对应的集合,其中为‘B‘的元素已被删除。
第三部分代码操作接口演示通过count方法接口统计当前集合容器中指定元素的个数。首先定义计数变量count存储统计的结果。当前集合对象实例调用count方法统计字符‘B‘的个数,将其值赋给count变量,最后打印输出count计数结果。由于统计的字符‘B‘在集合中不存在,所以统计结果为0。
第四部分代码接口实例中演示find方法来查找集合中对应的元素。调用find方法指定需要查找的元素为‘B‘字符,并将结果赋给迭代器iter,随后开始判断该迭代器是否指向end方法返回的迭代器指向元素。一旦判断为真,则表明没有找到当前字符‘B‘,内部迭代器指向了集合中end方法返回指向的位置。由于元素‘B‘不存在,则该判断成立。随后打印输出相应的没有找到元素的提示信息。
第五部分代码接口实例操作演示了lower_bound方法。根据传入的元素返回指向元素迭代器,将该迭代器指向赋给iter,最后为了演示该迭代器变化的结果则打印输出其迭代器指向的元素,以示验证。由于传入的元素为‘E‘存在于集合中,则返回当前元素的迭代器,最后打印输出结果依然为元素‘E‘。
最后调用clear方法清空当前集合容器charValue中所有元素,随后调用其empty方法根据返回的结果打印输出对应的提示信息。
16.2.10 map映射
关联容器中set容器适合单个元素值的存取,而map容器则提供了键/值对的存储。map容器有时被称为关联数组。map中的元素提供了键值对操作,键对应用着索引时使用的标号,值对应着键存储的数据,是可供被检索查找的具体数据值。map与set容器相同,其中元素都是以有序的方式存储的。因此,map容器支持高效率的检索与存储操作,通常寻求高效存储检索需求时可以使用map容器。
map容器同样提供了多个不同的构造函数实现,根据实际需要可以定义实现不同的对象实例。下面介绍使用map容器中提供的多个构造函数定义相应的对象实例。实现代码如下。
#include <map>
std::map;
map<int,string> testMap1;
map<int,string> testMap2(testMap1);
map<int,string>testMap3(firstIter,endIter);
与前面介绍的容器基本类似,map提供的构造函数接口基本也是这几种,不同的是参数实例化时需要至少指定两个类型,一个为map中的键类型,另一个则为键对应的数据类型。
q 第一个实例testMap1,参数实例中int型表示其键的类型,string表示键对应的数据类型,该map对象实例为空。
q 第二个构造对象实例则是采用拷贝构造函数。通过已经定义的map对象实例testMap1,拷贝构造成新的对象testMap2。该对象实例中拥有与testMap1同样的内容。
q 第三个map对象实例中同样的参数实例化,只是采用迭代器firstIter与endIter区间的元素初始化该对象testMap3。
16.2.11 map基本操作演示
表16.5列出基本的map容器提供的接口操作。
表格16.5 map容器公开接口方法说明
接口方法名称 |
基本功能说明 |
begin() |
放回当前map容器第一个元素指向的迭代器 |
count(value) |
返回当前map容器中元素value的次数 |
equal_range(value) |
返回当前map容器中value元素第一次出现与最后一次出现的两个迭代器 |
erase(value) |
删除map容器中映射为value的元素 |
erase(iter) |
删除map容器中迭代器iter指向的元素 |
erase(firstIter,endIter) |
删除map容器中迭代器firstIter到endIter区间的映射元素 |
clear() |
清空当前map容器中所有元素 |
empty() |
判断当前map容器是否为空,如果为空则返回true |
end() |
返回当前map容器最后一个映射元素的迭代器 |
find(value) |
返回当前map容器中指向元素value的迭代器,如果查找不到则返回方法end()方法所指向的迭代器 |
insert(iter,pair) |
当前map容器中iter指向的位置插入键值对pair,并且返回指向该键值对的迭代器 |
insert(firstIter,endIter) |
当前map容器中插入迭代器firstIter到endIter区间的元素 |
insert(pair) |
当前map容器中插入相应的pair对元素 |
lower_bound(value) |
如果value元素在当前集合中则返回当前元素指向的迭代器,如果value元素不在集合中则返回指向下一个元素的迭代器 |
upper_bound(value) |
如果value元素在当前集合中则返回下一个元素指向的迭代器,如果不存在也返回下一个元素指向的迭代器 |
max_size() |
返回当前map容器中最大的容量 |
rbegin() |
返回当前map容器反向的指向第一个元素的迭代器 |
rend() |
返回当前map容器反向的指向最后一个元素的迭代器 |
size() |
返回当前map容器中实际元素的数量 |
swap(testMap) |
交换当前map容器与testMap容器内容 |
key_comp() |
返回当前map容器的类型为key_compare的对象,该类型用于map容器中键的排序 |
value_comp() |
返回当前map容器类型为value_compare的对象,该类型用于map容器中值得排序 |
map容器提供的接口方法操作与set容器类似,只是map中接口总是操作着键/值对。下面通过一个完整实例简单演示map容器中几个方法接口的实际应用情况。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1605.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1605
* 源文件chapter1605.cpp
* map容器基本操作实例
*/
#include <iostream>
#include <map>
using namespace std;
int main()
{
//第一部分代码
map<int,char> testMap; //定义整型和字符型键值对的map对象testMap
map<int,char>::iterator iter; //定义同类型的迭代器iter
for(int i = 0;i < 5;i++) //循环控制结构,通过i变量值从0到5循环5次
{
testMap.insert(map<int,char>::value_type(i,65+i)); //通过insert()方法插入元素
}
cout<<"map elements:"<<endl; //提示打印map容器信息
for(iter = testMap.begin();iter != testMap.end();iter++) //通过循环控制遍历容器testMap
{
cout<<iter->first<<""<<iter->second<<endl; //输出testMap容器键值对值
}
//第二部分代码
testMap.insert(map<int,char>::value_type(8,65+4)); //通过insert方法在键为8位置插入值65+4
testMap.insert(map<int,char>::value_type(7,65+4)); //通过insert方法在键为7位置插入值65+4
testMap[6] = 65+4; //通过下标方式将65+4值放入键为6的位置
cout<<"map elements:"<<endl; //提示输出map容器内容
for(iter = testMap.begin();iter != testMap.end();iter++) //通过循环控制遍历容器testMap
{
cout<<iter->first<<""<<iter->second<<endl; //输出testMap容器键值对值
}
//第三部分代码
iter = testMap.begin(); //通过begin()方法将map的首元素赋值iter指向
testMap.erase(iter); //通过erase()方法删除iter指定的容器内元素值
testMap.erase(++iter); //通过erase()方法删除iter指定的下一个容器内元素值
cout<<"map elements:"<<endl; //提示打印map容器信息
for(iter = testMap.begin();iter != testMap.end();iter++) //通过循环控制遍历容器testMap
{
cout<<iter->first<<""<<iter->second<<endl; //输出testMap容器键值对值
}
//第四部分代码
testMap.clear(); //通过clear()方法清空map容器
if(testMap.empty()) //通过empty()方法判断容器是否为空
{
cout<<"The Map testMap isempty!"<<endl; //为空输出提示信息
}
else
cout<<"haveelements!"<<endl; //不为空输出提示信息
return 0;
}
本实例主要在主函数中实现map常见基本操作使用情况,程序中主要使用了map容器中基本的插入、删除数据操作。具体程序运行讲解见后面程序剖析。
2.编辑makefile
Linux平台下需要编译的源文件为chapter1605.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1605.o
CC=g++
chapter1605: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1605
clean:
rm-f chapter1605 core $(OBJECTS)
submit:
cp-f -r chapter1605 ../bin
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c-o chapter1605.o chapter1605.cpp
g++ chapter1605.o -g -o chapter1605
[developer @localhost src]$ make submit
cp -f -r chapter1605../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1605
map elements:
0 A
1 B
2 C
3 D
4 E
map elements:
0 A
1 B
2 C
3 D
4 E
6 E
7 E
8 E
map elements:
2 C
3 D
4 E
6 E
7 E
8 E
The Map testMap is empty!
4.程序剖析
本实例主要演示容器map的各类操作使用方法,下面将会分为X个部分来剖析讲解实例代码。
第一部分代码实例中首先定义空的map容器对象testMap,该容器键的类型为int型,而对应的数据值则为字符型,另外定义该map容器对象的迭代器iter。通过for循环控制结构采用insert方法根据变量i向map容器中插入键值对的元素。其中,键为递增的i变量,65+i转换为字符为键对应的数据值。随后通过迭代器iter遍历当前map容器,打印testMap中的键值对。其中,迭代器下first表示键的内容,而iter下的second表示键对应的数据。
第二部分代码示例主要演示map容器中插入元素的方法,主要采用两种方式:一种为insert方法,而另一种则为采用下标操作符的方式。实例代码中首先采用insert方法分别在位置键为8和7处插入相应的数据(65+4转换的字符)。随后,采用重载实现的下标操作符,下标操作符中键为6,对应的数据值直接赋值即可。插入操作作完后,通过迭代器遍历容器testMap打印输出验证插入元素。此时对应的6、7、8键上添加了元素字符‘E‘。
第三部分代码实例演示map容器中删除元素的操作。首先将迭代器iter定位到testMap首位置,随后调用erase方法,删除iter指向的元素。随后,同样调用erase方法,优先执行++运算将iter定位到第二个位置,删除掉对应位置的元素。此时根据迭代器iter遍历打印输出testMap容器中的键值对,元素中少了键为0、1的键值对。
第四部分代码最后调用clear方法清空testMap容器中的所有元素。随后,通过empty方法判断当前容器是否为空,如果为空则返回true,打印输出对应的提示信息。
16.2.12 multiset、multimap多重集合与映射
STL关联容器容器set与map提供存放数据元素,set中要求存放的数据元素与键值可以是同一个,另外存入的数据元素不能重复。而map容器存储键值对时,一个特定的键只能与一个元素相互关联。针对以上限制,STL库提供了multiset与multimap容器操作。multiset容器则允许集合中存在同样重复的元素,而multimap容器允许重复的键值对存在,键可以重复,键对应的关联数据也可以是多种重复的。下面通过一个完整实例,演示multiset与multimap容器区别于set与map的特性,实例代码如下。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1606.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示
/**
* 实例chapter1606
* 源文件chapter1606.cpp
* multiset、multimap容器基本操作实例
*/
#include <iostream>
#include <map>
#include <set>
using namespace std;
int main()
{
//第一部分代码
multiset<int> testSet; //定义整型类型集合容器testSet
multimap<int,char> testMap; //定义整型和字符型键值对的map容器testMap
multiset<int>::iterator iter1; //定义集合容器迭代器iter1
multimap<int,char>::iterator iter2; //定义map容器迭代器iter2
for(int i = 0;i < 5;i++) //循环结构控制,变量i从0到值5,循环5次
{
testSet.insert(i); //通过insert()方法插入元素i进入集合testSet容器
testMap.insert(multimap<int,char>::value_type(i,65+i)); //通过insert()向testMap容器插入元素值
}
cout<<"mulitiset elements:"; //提示打印输出容器mulitiset元素
for(iter1 = testSet.begin();iter1 != testSet.end();iter1++) //通过for循环遍历容器元素
{
cout<<*iter1<<" "; //输出迭代器指向元素值
}
cout<<endl;
//第二部分代码
cout<<"mulitimap elements:"<<endl; //提示打印输出容器mulitimap元素
for(iter2 = testMap.begin();iter2 != testMap.end();iter2++) //通过for循环遍历容器元素
{
cout<<iter2->first<<""<<iter2->second<<endl; //输出迭代器指向的元素值
}
iter1 = testSet.begin(); //通过begin()方法获取容器testSet的首元素位置赋值给iter1指向
testSet.insert(iter1,1); //通过insert()方法在iter1指定位置处插入元素值1
cout<<"mulitiset elements:"; //提示打印输出容器testSet元素
for(iter1 = testSet.begin();iter1 != testSet.end();iter1++) //通过for循环遍历容器元素
{
cout<<*iter1<<" "; //输出迭代器指向的元素值
}
cout<<endl;
testMap.insert(multimap<int,char>::value_type(1,65+3)); //通过insert()方法向容器testMap中插入元素值
cout<<"mulitimap elements:"<<endl; //提示打印输出容器元素内容
for(iter2 = testMap.begin();iter2 != testMap.end();iter2++) //通过for循环遍历容器元素
{
cout<<iter2->first<<""<<iter2->second<<endl; //输出迭代器指向的元素值
}
testSet.clear(); //通过clear()方法清空容器testSet
testMap.clear(); //通过clear()方法清空容器testMap
return 0;
}
本实例主要在主函数中实现multiset、multimap常见基本操作使用情况,程序中主要使用了multiset、multimap容器中基本的插入、删除数据操作,体现了与map、set等容器操作的基本区别。具体程序运行讲解见后面程序剖析。
2.编辑makefile
Linux平台下需要编译的源文件为chapter1606.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1606.o
CC=g++
chapter1606: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1606
clean:
rm-f chapter1606 core $(OBJECTS)
submit:
cp-f -r chapter1606 ../bin
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c-o chapter1606.o chapter1606.cpp
g++ chapter1606.o -g -o chapter1606
[developer @localhost src]$ make submit
cp -f -r chapter1606../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1606
mulitiset elements:01 2 3 4
mulitimap elements:
0 A
1 B
2 C
3 D
4 E
mulitiset elements:01 1 2 3 4
mulitimap elements:
0 A
1 B
1 D
2 C
3 D
4 E
4.程序剖析
上述实例程序中演示了使用multiset与multimap容器存放重复数据元素的情况。下面将会将代码分为两个部分进行剖析讲解。
第一个部分代码首先定义了容器multiset与multimap对象实例。其中,multiset容器采用int型参数实例化对象为testSet;而multimap容器则采用键为int型,而对应的数据为char型的对象实例testMap。另外定义了基于上述两个容器之上的迭代器iter1与iter2。
之后通过for循环控制结构,分别向两个容器中插入5个元素。multiset容器对象testSet中循环存放着从0~1的不重复元素,而multimap容器对象testMap中循环存放着键为0~5,对应的数据元素为A~E的元素。插入操作完成后,通过各自的迭代器遍历访问其中的元素并打印输出各自的元素。
第二个部分代码随后将multiset容器的迭代器iter1指向该实例testSet容器对象的首位元素。调用insert方法在该位置插入值为1的元素,并且通过迭代器遍历访问并打印输出结果。该结果中此时允许存在重复的值为1的元素。而针对multimap容器,则使用testMap对象调用insert方法插入键为1对应的数据元素为‘D‘。此时,通过迭代器iter2遍历打印。输出结果中除了键可以重复以外,相应的数据元素也可以重复。
与set、map容器相比,multiset与multimap容器可以满足特殊需求情况下的应用。尤其是当所要处理的数据允许重复以及需要快速地存储与检索时,可以考虑使用multiset与multimap容器。
16.3 STL库迭代器
STL标准库中迭代器(Iterator)与指针很类似。当作用于相应的容器,可以实现遍历、修改容器元素等操作。STL中迭代器是单独实现的组件,用户可以根据需要定义作用于不同容器的迭代器。下面将会分小节讲述STL标准库中提供的各种迭代器。
16.3.1 STL迭代器简介
STL标准库中总共提供了五类不同迭代器类型,每个迭代器类型都针对不同的处理需求提供了相应的功能供开发者根据不同的情况使用。下面分别简单介绍下五类迭代器基本情况。
q 第一类迭代器称为输入迭代器(Input Iterator)。该类迭代器主要用于从容器当中取数据,支持单向从容器首部位置向后递增遍历。该类迭代器相对较为灵活,可以通过变量递增方式实现指向的跨越访问,并且该类迭代器也支持相应的比较运算操作。
q 第二类迭代器称为输出迭代器(Output Iterator)。该类迭代器主要用于向容器中写入数据,同样也只能单次递增的从容器的首部向后遍历元素。
q 第三类迭代器称为正向迭代器(Forward Iterator)。该类迭代器结合了输入/输出两种迭代器功能,同时还可以记录该迭代器在容器中的位置信息,以供后续处理。
q 第四类迭代器称为双向迭代器(Bidirectional Iterator)。该类迭代器不仅仅拥有了输入输出迭代器的功能,同时还支持从容器尾部向首部反向遍历的操作,并且支持可以多次遍历容器的功能。
q 第五类称为随机访问迭代器(Random Access Iterator)。它可以根据需要,随机地访问容器中的元素,并且可以正向或者反向的任意递增间隔的跳转。
从上述介绍看来,五类迭代器从基本的输入输出开始,一直到随机访问迭代器,其提供的功能越来越强大。基本上所有的STL容器都支持上述五种不同类别的迭代器操作。根据实际应用的需要,用户来选择不同类别的迭代器。初学者可以将迭代器看作指针来访问具体的容器内容。实际上,STL迭代器就是针对访问容器的指针的封装。
16.3.2 输入/输出迭代器
输入/输出迭代器用于处理容器的数据的读取与写入操作。
q 输入迭代器为应用程序处理容器中的数据提供了操作接口。使用该迭代器,可以从容器序列中读取元素值。该迭代器本身可以通过++运算符改变指向(前置自增、后置自增方式都支持),并且可以使用比较运算符进行比较运算。
q 输出迭代器提供了接收外部程序处理的数据,往容器序列中写入这些数据。另外该迭代器也可以通过递增运算来改变其位置指向(前置自增、后置自增方式),并且也可以被引用输出或者参与程序其它部分操作。
输入、输出迭代器划分主要从迭代器支持的功能上进行区别,输入迭代器支持后置自增操作(point++)、前置自增(++point)、迭代器间接运算符(*point,只作为表达式右值使用)、迭代器之间赋值(point1=point2)以及迭代器比较操作(point1==point2,point1!=point2);输出迭代器支持后置自增和前置自增操作以外,还支持迭代器间接运算符(*point,作为表达式左值,用于存放数据)以及迭代器赋值操作(point1=point2)。
了解了输入输出迭代器基本概念之后,通过一个完整实例来演示两种迭代器具体操作以及之间的使用区别。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1609.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1609
* 源文件chapter1609.cpp
* list容器输入输出迭代器基本操作实例
*/
#include <iostream>
#include <string>
#include <list>
using namespace std;
int main()
{
//第一部分代码
stringdata,data1; //定义字符串两个对象,分别为data和data1
list<string> stringList; //定义字符串类型的List队列对象
cout<<"Please input some stringvalues into StringList(‘#‘ is end):"<<endl; //提示输入字符串类型的List元素
cin>>data; //从键盘输入字符串变量
//第二部分代码
while(data!= "#") //判断输入以‘#’为结束符,如果输入字符串不为#,就会不断加入List
{
stringList.push_front(data); //通过List容器push_front方法,将输入字符串变量加入List头部
cout<<"Please input some stringvalues into StringList(‘#‘ is end):"<<endl; //提示继续输入字符串变量值
cin>>data; //从键盘输入字符串变量
}
cout << "StringList value‘s: "; //提示打印输出字符串类型的List里面元素值
list<string>::iterator iter,iter1; //通过iterator定义两个字符串类型List迭代器
iter1 = stringList.end(); //迭代器iter1通过赋值的方式指向List最后一个元素
//第三部分代码
for (iter = stringList.begin(); iter != iter1;iter++) //通过迭代器iter循环遍历List各个元素
{
cout << *iter; //通过间接运算符输出遍历到List的迭代器指向的值
*iter = "Hello"; //将迭代器*iter作为左值在表达式中出现,将常量字符串Hello作为右值
data1 = *iter; //将赋值后的迭代器iter通过间接运算符将其作为右值赋给字符串对象data1
}
cout << endl; //循环遍历结束后,添加输出结束换行符
cout<<"data1:"<<data1<<endl; //打印输出字符串对象data1内容
return0;
}
2.编辑makefile
Linux平台下需要编译源文件为chapter1609.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1609.o
CC=g++
chapter1609: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1609
clean:
rm-f chapter1609 core $(OBJECTS)
submit:
cp-f -r chapter1609 ../bin
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c -ochapter1609.o chapter1609.cpp
g++ chapter1609.o -g -o chapter1609
[developer @localhost src]$ make submit
cp -f -r chapter1609 ../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1609
Please input some string values into StringList(‘#‘is end):
jack
Please input some string values intoStringList(‘#‘ is end):
lily
Please input some string values intoStringList(‘#‘ is end):
lihua
Please input some string values intoStringList(‘#‘ is end):
hanmm
Please input some string values into StringList(‘#‘is end):
#
StringList value‘s: hanmmlihualilyjack
data1:Hello
4.剖析程序
本实例主要通过STL中提供的List容器来演示输入/输出迭代器实现的基本功能。STL库中的迭代器的不同类型都是通过iterator来统一定义的。不同种类的迭代器主要体现在不同的容器上面。容器List是支持双向迭代器功能的,因为双向迭代器包含了输入/输出迭代器的功能,因此此实例中主要通过List来演示输入输出的迭代器功能使用。下面将会通过
第一部分代码实例程序主要在主函数内部实现,具体代码实现首先通过标准的字符串类string定义两个字符串对象data和data1。通过STL中list定义字符串类型的容器对象stringList,随后通过输出流对象cout提示用户输入字符串变量值。然后通过输入流对象cin将键盘输入的字符串变量值放入字符串对象data中。
第二部分代码通过while循环结构,在其中条件判断中判断字符串对象data内容是否为结束符“#”。如果不为“#”,则在循环体的内部通过容器list的push_front方法将字符串对象data内容放入到容器的头部。随后再通过cout和cin对象来提示和输入数据。
代码中通过cout对象提示程序接下来会将容器list中的元素数据循环打印出来。随后,通过iterator关联容器list定义出字符串类型的迭代器对象iter和iter1。STL的list字符串类型容器对象stringList通过end()方法将指向该容器的最后一个元素的迭代器返回,通过赋值运算将该迭代器赋值给迭代器iter1,即此时迭代器iter1指向了容器对象stringList最后一个元素。
第三部分代码通过for循环结构,循环中首先将list容器对象stringList的第一个元素通过begin()方法,将指向容器对象stringList首元素的迭代器赋值给迭代器对象iter,判断当前的iter指向元素是否与迭代器对象iter1中的尾部元素相同。如果不同,则执行for循环结构体中的代码片段,之后通过iter++运算遍历下一个元素。
程序for循环体的内部,首先通过cout输出流对象输出迭代器中指向的数据元素值,在输出流中通过间接运算符来取得迭代器iter中的数据元素值并打印出来。之后的代码通过将*iter作为表达式的左值来存放常量字符串“Hello”,并且将重新赋值后的*iter作为表达式右值赋值给字符串对象data1。
循环体处理结束后,通过endl来实现打印数据换行,同时打印输出字符串对象data1的内容。上述代码程序通过make执行之后编译无误会产生相应的可执行程序,在程序的bin目录中执行可执行程序,根据屏幕输出提示,敲入字符串变量将会按照迭代器获取到的数据打印情况来验证程序处理逻辑。
总结上述实例,迭代器提供的输入、输出、正向、双向和随机类型都是通过iterator关联相应的容器定义对象时就决定了哪类迭代器,不同的STL容器具备的迭代器类型不同,如上述容器list支持双向类型迭代器。需要注意的是,输入/输出迭代器除了上述统一对外定义方式以外,还提供了特殊的符合输入、输出迭代器功能的迭代器istream_iterator(输入类型迭代器)和ostream_iterator(输出类型迭代器)。该部分迭代器涉及文件流操作,此处暂时不过多讲述,初学者只需要理解对外统一的迭代器操作使用方式即可。
16.3.4 双向迭代器
STL库中双向迭代器除了同时具备输入/输出迭代器功能以外,还支持从容器尾部向首部反向遍历的操作以及通过“--”运算符支持前置递减和后置递减的操作。支持双向迭代器功能的容器除了前面实例介绍的list以外,还有set、map容器都是支持双向迭代器操作。
下面将会通过STL中map容器的上双向迭代器操作实例,演示双向迭代器在具体容器中使用情况。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1610.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1610
* 源文件chapter1610.cpp
* map容器双向迭代器基本操作实例
*/
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
//第一部分代码
map<int,string> stringMap,stringMap1; //定义两个map对象,其中键为int整型数据,键值为字符串数据
string data; //定义字符串对象data
int i= 1; //定义整型变量i,同时初始化为数值1
cout<<"Please input some stringvalues into stringMap(‘#‘ is end):"<<endl; //提示输入map值,以#为结束符
cin>>data; //从键盘输入字符串变量data
while(data != "#") //通过while循环判断输入字符串是否为“#”
{
stringMap[i]= data; //不为结束符,通过数组方式来将输入的字符串变量值放入map中
cout<<"Please input some stringvalues into stringMap(‘#‘ is end):"<<endl; //提示输入map值
cin>>data; //从键盘输入字符串变量data
i++; //将map中的键i通过后置自增方式增加1
}
cout << "stringMap value‘s:"<<endl; //提示打印输出map内容值
map<int, string>::iterator iter; //定义同类型的map迭代器为iter
for (iter = stringMap.begin();iter != stringMap.end();iter++) //for循环中,通过iter迭代器遍历map容器
{
cout << (*iter).first <<" -> "; //输出流打印输出map容器中的键
cout << (*iter).second ; //输出流打印输出map容器中的键对应的值
}
cout<<endl; //循环遍历结束后,添加输出结束换行符
//第二部分代码
i = 1; //整型变量i,重新初始化为数值1
cout<<"Please input some stringvalues into stringMap1(‘#‘ is end):"<<endl; //提示输入map值
cin>>data; //从键盘输入字符串变量data
while(data != "#") //通过while循环判断输入字符串是否为“#”
{
stringMap1.insert(map<int,string>::value_type (i, data)); //通过容器map的insert方法插入元素数据
cout<<"Please input some stringvalues into stringMap1(‘#‘ is end):"<<endl;// 提示输入map值
cin>>data; //从键盘输入字符串变量data
i++; //将map中的键i通过后置自增方式增加1
}
cout << "stringMap1 value‘s:"<<endl; //提示打印输出map内容值
map<int, string>::reverse_iterator iter1; //定义同类型的map反向迭代器为iter
for (iter1 = stringMap1.rbegin();iter1 !=stringMap1.rend(); iter1++) // for循环中,通过iter1迭代器反向
//遍历map容器
{
cout << (*iter1).first <<" -> "; //输出流打印输出map容器中的键
cout << (*iter1).second ; //输出流打印输出map容器中的键对应的值
}
cout<<endl; //循环遍历结束后,添加输出结束换行符
return 0;
}
2.编辑makefile
Linux平台下需要编译源文件为chapter1610.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1610.o
CC=g++
chapter1610: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1610
clean:
rm-f chapter1610 core $(OBJECTS)
submit:
cp-f -r chapter1610 ../bin
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c -ochapter1610.o chapter1610.cpp
g++ chapter1610.o -g -o chapter1610
[developer @localhost src]$ make submit
cp -f -r chapter1610 ../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1610
Please input some string values into stringMap(‘#‘is end):
jack
Please input some string values into stringMap(‘#‘is end):
john
Please input some string values into stringMap(‘#‘is end):
lily
Please input some string values into stringMap(‘#‘is end):
poli
Please input some string values into stringMap(‘#‘is end):
#
stringMap value‘s:
1 -> jack
2 -> john
3 -> lily
4 -> poli
Please input some string values intostringMap1(‘#‘ is end):
lilei
Please input some string values intostringMap1(‘#‘ is end):
lijie
Please input some string values intostringMap1(‘#‘ is end):
lijia
Please input some string values intostringMap1(‘#‘ is end):
lily
Please input some string values intostringMap1(‘#‘ is end):
#
stringMap1 value‘s:
4 -> lily
3 -> lijia
2 -> lijie
1 -> lilei
4.剖析程序
上述实例程序主要通过演示双向迭代器具体操作,来提供双向迭代器一般应用方法。实例程序主要分为两个部分剖析讲解。
第一部分代码实例程序主要在主函数内部实现,具体代码实现首先定义两个map容器对象stringMap和stringMap1。该map容器键值分别为整型值和对应的字符串变量。通过string定义一个字符串对象data,另外通过int定义整型变量i,同时初始化值为1。
随后通过输出流对象cout提示用户输入字符串变量值。然后通过输入流对象cin将键盘输入的字符串变量值放入字符串对象data中。接下来通过while循环结构,在其中条件判断中判断字符串对象data内容是否为结束符“#”。如果不为“#”,则在循环体的内部通过类似数组方式下标将数据放入map容器中。由于map是键值成对的存储方式,因此左值中以数组方式组织,i变量为键即key。表达式右值为字符串对象data,即键对应的值value。通过这种数组式的组织数据方式,将字符串类型的数据放入map容器。随后再通过cout和cin对象来提示和输入数据,同时将键(key)自增1。
代码中通过cout对象提示程序接下来会将容器map中的元素数据循环打印出来,随后通过iterator关联容器map定义出相同类型的迭代器对象iter。通过for循环结构,循环条件中首先将map容器对象stringMap的第一个元素通过begin()方法,将指向容器对象stringMap首元素的迭代器赋值给迭代器对象iter,判断当前的iter指向元素是否与stringMap尾部元素相同。如果不同,则执行for循环结构体中的代码片段,之后通过iter++运算遍历下一个元素。
程序for循环体的内部,首先通过cout输出流对象输出迭代器指向的数据元素键(key)、值(value),在输出流中通过间接运算符“*”来取得迭代器iter中first和second数据值打印出来。循环体处理结束后,通过endl来实现打印数据换行。
第二部分代码在通过迭代器iter遍历完map容器stringMap之后,将整型变量i重新初始化为整型值1。接着通过输出流对象cout提示用户输入字符串变量值。然后通过输入流对象cin将键盘输入的字符串变量值放入字符串对象data中。接下来通过while循环结构,在其中条件判断中判断字符串对象data内容是否为结束符“#”,如果不为“#”在循环体的内部通过map容器的insert方式入数据到map容器中。随后再通过cout和cin对象来提示和输入数据,同时将键(key)自增1。
代码中通过cout对象提示程序接下来会将容器map中的元素数据循环打印出来,随后通过反向迭代器reverse_iterator关联容器map定义出相同类型的迭代器对象iter1。通过for循环结构,循环条件中首先将map容器对象stringMap的最后一个元素通过rbegin()方法,将指向容器对象stringMap尾部元素的迭代器赋值给迭代器对象iter1,判断当前的iter1指向元素是否与stringMap首元素相同,如果不同则执行for循环结构体中的代码片段,之后通过iter1++运算遍历下一个元素。
上述代码程序通过make执行之后编译无误会产生相应的可执行程序,在程序的bin目录中执行可执行程序,根据屏幕输出提示,敲入字符串变量将会按照迭代器获取到的数据打印情况来验证程序处理逻辑。
16.3.5 随机访问迭代器
随机访问迭代器(RandomAccess Iterator),可以根据需要随机的访问容器中的元素,并且可以正向或者反向的任意递增间隔的跳转。
STL库中随机迭代器除了同时具备输入、输出迭代器、双向迭代器功能以外,还支持随机访问,特别是这种迭代器拥有iterator运算,即你可以对一个随机类型的迭代器进行加、减、比较等操作。容器vector、deque和string容器的迭代器属于随机迭代器。
随机迭代器除了支持前面输入、输出、双向迭代器功能以外,还会根据随机访问的特性支持迭代器偏移递增i位(point+=i),迭代器偏移递减i位(point-=i),迭代器直接返回指向元素的i位(point[i]),迭代器比较运算操作(point1<point2,point1<=point2,point1>point2,point1>=point2)。
下面将会通过STL中vector容器的上随机迭代器操作实例,演示随机迭代器在具体容器中使用情况。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1611.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1611
* 源文件chapter1611.cpp
* vector容器随机迭代器基本操作实例
*/
#include <iostream>
#include <vector>
using namespace std;
int main()
{
//第一部分代码
vector<int>intvec; //定义整型vector对象intvec
intdata; //定义整型变量data
cout<<"Pleaseinput some int values into intvec(‘0‘ is end):"<<endl; //提示输入map值,以0为结束符
cin>>data; //从键盘输入整型变量data
while(data != 0) //通过while循环判断输入整型值是否为0
{
intvec.push_back(data); //不为结束符,通过vector的push_back方法将data放入容器尾部
cout<<"Please input some intvalues into intvec(‘0‘ is end):"<<endl; //提示输入map值,以0为结束符
cin>>data; //从键盘输入整型变量data
}
cout << "intvec value‘s:"<<endl; //提示打印输出vector内容值
vector<int>::iterator iter; //定义整型vector随机迭代器iter
for (iter = intvec.begin();iter !=intvec.end(); iter++) // for循环中,通过iter迭代器遍历vector容器
{
cout << *iter<<endl; //通过间接运算符输出遍历到vector的迭代器指向的值
}
//第二部分代码
vector<int>intvec1; //定义整型vector对象intvec1
vector<int>::iterator iter1 =intvec1.begin(); //定义整型vector随机迭代器iter,同时指向intvec1首元素
intvec1.insert(iter1, intvec.begin(),intvec.end());//通过vector的insert方法将容器intvec内容插入容器intvec1
iter1 = intvec1.begin() + 3; //将随机迭代器iter1指向容器intvec1的第三个位置
intvec1.insert(iter1, 1, 100); //容器intvec1通过insert方法将其中第三个元素替换为100
iter1 = intvec1.begin() + 1; //重新将迭代器iter1指向容器intvec1[1]的位置
intvec1.erase(iter1, iter1 + 2); //容器intvec1通过erase方法将intvec1[1]~intvec1[2]元素删除
cout << "intvec value‘s:"<<endl; //提示打印输出vector内容值
for (iter1 = intvec1.begin();iter1 <intvec1.end(); iter1++) // for循环中,通过iter1迭代器遍历vector容器
{
cout << *iter1<<endl; //通过间接运算符输出遍历到vector的迭代器指向的值
}
return 0;
}
2.编辑makefile
Linux平台下需要编译源文件为chapter1611.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1611.o
CC=g++
chapter1611: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1611
clean:
rm-f chapter1611 core $(OBJECTS)
submit:
cp-f -r chapter1611 ../bin
上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c -ochapter1611.o chapter1611.cpp
g++ chapter1611.o -g -o chapter1611
[developer @localhost src]$ make submit
cp -f -r chapter1611 ../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1611
Please input some int values into intvec(‘0‘ isend):
123
Please input some int values into intvec(‘0‘ isend):
234
Please input some int values into intvec(‘0‘ isend):
345
Please input some int values into intvec(‘0‘ isend):
456
Please input some int values into intvec(‘0‘ isend):
0
intvec value‘s:
123
234
345
456
intvec value‘s:
123
100
456
4.剖析程序
随机迭代器基本支持的功能在STL迭代器简介中已经简单介绍过,本实例主要演示了这些迭代器基本功能。实例程序主要在主函数内部实现,下面将会通过将程序分为两个部分来进行剖析讲解。
第一部分代码实现首先定义整型vector容器对象intvec。通过int定义一个整型变量data。随后通过输出流对象cout提示用户输入整型变量值。然后通过输入流对象cin将键盘输入的整型变量值放入整型变量data中。接下来通过while循环结构,在其中条件判断中判断整型变量data内容是否为结束符整型值0,如果不为整型值0则在循环体的内部通过vector中的push_back方法将data加入到容器intvec的尾部。
代码中通过cout对象提示程序接下来会将容器vector中的元素数据循环打印出来,随后通过iterator关联容器vector定义出相同类型的随机迭代器对象iter。通过for循环结构,循环条件中首先将vector容器对象intvec的第一个元素通过begin()方法,将指向容器对象intvec首元素的迭代器赋值给迭代器对象iter。判断当前的iter指向元素是否与intvec尾部元素相同,如果不同则执行for循环结构体中的代码片段,之后通过iter++运算遍历下一个元素。
第二部分代码片段中再次定义整型vector向量对象intvec1,同时定义同类型的随机迭代器iter1,并且将该迭代器指向容器intvec1的首元素。通过vector容器的insert方法,将之前定义的容器intvec从首元素到尾部元素都插入到新容器intvec1中去。
从将vector容器intvec所有元素都插入到新容器intvec1中后面的代码片段将会主要演示随机迭代器的特有功能。首先通过容器的begin方法返回指向容器intvec1的首元素迭代器加上3后,将迭代器iter1定位到容器intvec1的第三个位置上。
随后通过容器的insert方法将容器intvec1的第三个元素之后插入值100,之后通过intvec1的begin方法返回的首位元素的迭代器加上1,重新将迭代器iter1定位到容器intvec1的第一个位置上。随后通过erase方法,从容器intvec1第一个位置到第二个位置之间的元素删除。
最后通过cout对象提示将会遍历输出新容器所有元素,并且打印输出出来。照旧使用的for循环控制结构,内部循环条件不同的是迭代器判断是通过小于号来进行遍历范围限定的,通过迭代器之间的比较运算,将新容器intvec1内部的所有元素都遍历输出。
上述代码程序通过make执行之后编译无误会产生相应的可执行程序,在程序的bin目录中执行可执行程序,根据屏幕输出提示,敲入整型变量将会按照迭代器获取到的数据打印情况来验证程序处理逻辑。
16.4 STL库基本算法
标准C++STL库中算法组件为一个很重要的组成部分,该组件提供了大多数最常见的通用算法的实现,并且这些实现是经过很多测试试验并被公认在处理上是高效的。将这些最常见的算法通用化实现,最大的优势就是开发者在应用中不需要为具体的常见算法的实现而费神,只需要包含相应的头文件直接使用即可,不仅仅提高的软件开发的效率,同时还有助于软件重用性的提高。
16.4.1 STL库基本算法简介
根据STL标准库提供的文档,该库中的算法组件主要由于4个部分组成,即通用算法的实现划分为4类,分别应用对不同需求下的处理。
按照不同的特征处理划分第一类算法称之为非修正算法,其实就是提供的算法实现中不会涉及修改对应操作的容器中的任何元素,包括其位置等操作。这类算法主要为一些查找、统计、比较类操作实现,主要包含find()、find_first()、find_end()、count()、equal()等不需要进行操作的容器元素变动的算法。
第二类自然是与之对应的修正类算法,该类算法是在实际使用与具体的容器上时,需要通过适当的改变容器中内容从而实现相应的功能。这类针对容器处理需要修该的算法主要包括一些交换元素、替换元素、删除等操作实现,STL中主要包含copy()、swap()、remove()以及replace()等算法实现。
第三类自然是平时实际应用中较频繁的排序算法的提供,排序算法在实际应用中尤其是大数据量处理的软件中应用尤其突出,算法的处理效率适用场合决定着软件处理大数据量排序时的处理效率。STL库中针对排序提供了比较完备的算法实现,其中sort()、table_sort()等算法都以较高处理效率而著称。
最后一类是针对容器中元素的计算提出的数值计算算法,主要提供了容器内元素乘积之和、元素加法之和以及相邻的元素之差等算法基本操作。
STL标准库提供的通用算法实现基本都是高效稳定的,对于初学者来讲理解这些不同类算法的基本实现功能、基本操作应用,另外需要了解的即在应用开发中遇到问题时会分析从而采用最适合的算法来解决相应的问题即可。随着逐步深入学习,为了更加理解相应库算法的具体实现,从而更好地利好用STL库中算法,可以逐步的学会分析该库提供的算法组件实现的源码,从中获取组件设计实现的思路,提高编程修养。
下面将会按照算法组件中4个类别的算法,分为4个小节对应着讲述STL库算法组件提供的算法操作基本实现原理,以及在实际应用中使用方法,初学者可以参照并在实践中尽可能的思考更多的应用情况。另由于STL库提供的算法约70多种之多,篇幅限制的情况下会根据不同类别的算法挑部分作为典型作详细的讲述,其它类别的算法可以采用触类旁通根据演示的基本使用方式,从而掌握其应用。
16.4.2 非修正算法
通过前面介绍STL库中算法组件提供了不需要修改容器内元素的算法操作实现,算法组件中非修正算法主要包括了adjacent_find()、find()、find_end()、find_first()、count()、equal()、for_each()以及serch()等操作接口。
这类算法操作主要针对不需要修改容器序列操作的情况下提供的,其中最常见的要算在容器之上的查找操作了。STL算法组件针对查找操作提供了几种不同的操作算法接口,分别为adjacent_find()、find()、find_end()和find_first()。
其中adjacent_find()算法操作主要用于查找容器中是否存在相邻元素,该方法接口返回找到的一对容器中指向相同临近元素之一的迭代器,该方法需要提供操作容器的范围迭代器。
find()操作算法主要用于查找容器中某个特定元素,找到则返回该容器中第一个找到特殊元素位置的迭代器。同时通过find()算法,可以根据需要扩展出多个如find_end()、find_first()、find_first_of()等算法操作接口,分别在find()算法基础上增加不同的限制实现的算法操作接口。
同时修正类算法还提供如equal()这样比较容器对象的算法操作,允许使用同类型容器不同对象之间比较,内部实际实现了不同的对象比较运算,以及for_each()算法在迭代器指定范围内循环执行相应参数提供的方法的操作,供实际应用中使用。下面将会通过一个完整实例,演示部分非修正算法的使用情况,通过这部分算法接口的操作应用了解STL库算法组件的常见使用方法。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1613.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1613
* 源文件chapter1613.cpp
* 非修正类算法应用实例
*/
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
//第一部分代码
vector<int>testVector; //定义int整型类型vector容器testVector
vector<int>::iteratorfirstIter; //定义int整型类型迭代器firstIter
vector<int>::iteratorendIter; //定义int整型类型迭代器endIter
vector<int>::iteratortempIter; //定义int整型类型迭代器tempIter
for(inti = 0;i < 10;i++) //通过循环控制结构,变量i值从0到10循环10次
{
testVector.push_back(i); //通过容器提供的push_back方法将i变量值放入testVector
}
//find()算法演示
firstIter= testVector.begin(); //通过begin()方法将容器testVector首个元素赋值firstIter指向
endIter= testVector.end(); //通过end()方法将容器testVector末元素赋值endIter指向
tempIter = find(firstIter,endIter,8); //通过find()方法从容器首元素到尾元素之间查找元素值为8的元素
if(tempIter== testVector.end()) //判断是否遍历至容器尾部
{
cout<<"Don‘tfind element value‘s 8!"<<endl; //提示没有找到相应元素
}
else
cout<<"Findthis element:"<<*tempIter<<endl; //提示找到相应元素 //第二部分代码
//adjacent_find()算法演示
testVector.insert(++firstIter,2,10); //通过insert()方法向容器testVector中插入元素值
cout<<"vectorelements:"; //提示打印输出容器元素
for(firstIter= testVector.begin();firstIter != testVector.end();firstIter++) //循环遍历容器testVector
{
cout<<*firstIter<<""; //输出迭代器指向元素值
}
cout<<endl;
firstIter= testVector.begin(); //通过begin()方法将容器testVector首个元素赋值firstIter指向
endIter = testVector.end(); //通过end()方法将容器testVector末元素赋值endIter指向
tempIter= adjacent_find(firstIter,endIter); //通过adjacent_find()方法查找相邻相同元素
if(tempIter!= testVector.end()) //判断是否遍历至容器尾部
{
cout<<"Findpair elements:"<<"(" //找到则输出提示
<<*tempIter<<","<<*(tempIter+1)<<")"
<<"atloc:"<<tempIter - firstIter<<endl;
}
else
cout<<"notfind pair elements!"<<endl; //未找到,输出提示
//第三部分代码
//count()算法演示
firstIter= testVector.begin(); //通过begin()方法将容器testVector首个元素赋值firstIter指向
endIter = testVector.end(); //通过end()方法将容器testVector末元素赋值endIter指向
cout<<"countelement‘s 10:"<<count(firstIter,endIter,10)<<endl; //通过count方法计算容器中元素10的个数
return0;
}
2.编辑makefile
Linux平台下需要编译源文件为chapter1613.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1613.o
CC=g++
chapter1613: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1613
clean:
rm-f chapter1613 core $(OBJECTS)
submit:
cp-f -r chapter1613 ../bin
上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c -ochapter1613.o chapter1613.cpp
g++ chapter1613.o -g -o chapter1613
[developer @localhost src]$ make submit
cp -f -r chapter1613../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1613
Find this element:8
vector elements:0 10 10 1 2 3 4 5 6 7 8 9
Find pair elements:(10,10)at loc:1
count element‘s 10:2
4.剖析程序
本实例程序主要演示了非修正算法中find()、adjacent_find()以及count算法接口的操作应用。STL库中算法组件通过头文件#include<algorithm>包含,即可以在应用程序中使用提供的相应的算法接口操作。
第一部分代码在主程序中只要定义整型空的序列容器vector对象testVector,随后定义三个该类型的迭代器分别表示指向容器首位置、尾位置以及作为临时结果的迭代器。
随后的for循环控制结构中,通过push_back方法向向量容器尾部循环添加对应的元素,将定义的两个迭代器通过容器提供的begin()与end()方法分别定位到vector容器的首部和尾部。随后直接调用算法操作接口find(),该算法接口共三个参数,两个迭代器分别表示所要查找的元素区间,最后一个参数则表示所要查找的元素值。该方法调用中迭代器定位至容器首部与尾部,同时查找元素为整型值8,调用返回找到元素的指向迭代器,将其结果赋给结果迭代器tempIter。
调用find方法之后将方法结果返回值赋给结果迭代器tempIter,之后判断该迭代器,并根据该结果迭代器的判断情况来说明是否找到相应的元素,以及通过解引用直接打印输出找到的元素。由于元素8存在于容器队列testVector中,if判断条件不成立,直接打印输出找到的元素值。
第二部分代码主要演示adjacent_find方法查找相邻相同元素,首先通过testVector调用insert方法在指定的首部之后的元素位置插入2个值为10的元素。此时通过迭代器firstIter遍历容器并打印输出容器中的内容为“0 10 10 1 2 3 4 5 6 7 8 9”。
随后迭代器firstIter与endIter重新定位至容器的首部与尾部,通过调用adjacent_find方法演示相邻相同元素查找的算法操作,该算法接口主要包含两个参数,分别为需要查找的元素范围迭代器。程序中传入重新定位的两个迭代器,即表明查找操作将会在该容器的首部到尾部区间之间进行。由于该方法将会返回指向相邻相同元素的一个迭代器,将该方法结果直接赋给结果迭代器tempIter,随后开始判断该迭代器是否指向了容器的尾部,如果指向尾部则说明没找到相邻相同的元素,如果没有则打印相邻的两个元素值以及该元素所处的位置。
第三部分代码演示非修正算法中count统计序列相同元素个数的算法操作,将迭代器重新定位后,直接在流输出中调用count方法查找元素为10的个数,统计范围自然为从容器首部到尾部区间。
16.4.3 修正算法
STL库中修正算法主要针对数据元素序列处理可能涉及修改的情况,主要包括序列中元素的填充、交换、拷贝、删除与替换等。部分修正算法可以通过表格16.6作一个简单的说明,如下所示。
表格16.6 STL部分修正算法简介
算法接口 |
说明 |
fill(firstIter,endIter,value) |
将元素值value填补到迭代器所指向区间中 |
copy(firstiter,endIter,firstIter1) |
将迭代器firstIter与endIter区间元素拷贝至迭代器firstIter1所指向的序列的开始之处 |
copy_backward(firstIter,endIter,firstIter1) |
将迭代器firstIter与endIter区间元素拷贝至迭代器firstiter1所指向的序列开始之初,但是从最后一个元素往前逐个拷贝赋值 |
remove(firstIter,endIter,value) |
删除迭代器firstIter与endIter区间内值为value的所有元素 |
replace(firstIter,endIter,value1,value2) |
区间firstIter与endIter之间元素,用value1替换所有的value2 |
reverse(firstIter,endIter) |
将迭代器区间firstIter与endIter之间的元素反向排列 |
swap(iter1,iter2) |
交换迭代器iter1与iter2所指向的元素 |
unique(firstIter,endIter) |
去除迭代器firstIter与endIter区间内重复的相邻元素 |
上述表格大致列出了STL库中修正类算法常用的几个算法操作,并作了简单的操作说明。修正类算法主要针对序列中删除、修改、交换等操作作了高效算法的实现,下面将会通过一个完整操作修正类算法的实例,根据STL中提供的算法的基本接口配合简单的说明在实际程序中应用这些算法。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1614.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1614
* 源文件chapter1614.cpp
* 修正算法应用实例
*/
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
//第一部分代码
vector<int>testVector; //定义int整型类型vector容器对象testVector
vector<int>::iteratorfirstIter; //定义int整型类型容器迭代器firstIter
vector<int>::iteratorendIter; //定义int整型类型容器迭代器endIter
for(inti = 0;i < 10;i++) //循环控制结构,从变量i为0开始到10,循环10次
{
testVector.push_back(i); //通过push_back将元素值i放入容器testVector
}
cout<<"vectorelements:"; //提示打印输出容器内容
for(firstIter= testVector.begin();firstIter != testVector.end();firstIter++) //通过for循环控制遍历容器内容
{
cout<<*firstIter<<""; //输出迭代器指向元素值
}
cout<<endl;
firstIter= testVector.begin(); //通过begin()将容器testVector首元素赋值给firstIter指向
endIter = testVector.end(); //通过end()将容器testVector首元素赋值给endIter指向
fill(firstIter,firstIter+3,8); //通过fill()方法在容器指定位置填充元素值8
cout<<"vectorelements:"; //提示输出容器内容
for(firstIter= testVector.begin();firstIter != testVector.end();firstIter++) //通过for循环遍历容器
{
cout<<*firstIter<<""; //打印输出迭代器指向值
}
cout<<endl;
//第二部分代码
firstIter= testVector.begin(); //通过begin()将容器testVector首元素赋值给firstIter指向
endIter = testVector.end(); //通过end()将容器testVector首元素赋值给endIter指向
reverse(firstIter,endIter); //通过reverse()方法颠倒容器中的元素
cout<<"vector elements:"; //提示输出容器内容
for(firstIter = testVector.begin();firstIter!= testVector.end();firstIter++) //通过for循环遍历容器
{
cout<<*firstIter<<""; //打印输出迭代器指向值
}
cout<<endl;
//第三部分代码
endIter =unique(testVector.begin(),testVector.end()); //通过unique()方法过滤相邻相同元素
cout<<"vector elements:"; //提示输出容器内容
for(firstIter = testVector.begin();firstIter !=endIter;firstIter++) //通过for循环遍历容器
{
cout<<*firstIter<<""; //打印输出迭代器指向值
}
cout<<endl;
return0;
}
2.编辑makefile
Linux平台下需要编译源文件为chapter1614.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1614.o
CC=g++
chapter1614: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1614
clean:
rm-f chapter1614 core $(OBJECTS)
submit:
cp-f -r chapter1614 ../bin
上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c -ochapter1614.o chapter1614.cpp
g++ chapter1614.o -g -o chapter1614
[developer @localhost src]$ make submit
cp -f -r chapter1614../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1614
vector elements:0 1 2 3 4 5 6 7 8 9
vector elements:8 8 8 3 4 5 6 7 8 9
vector elements:9 8 7 6 5 4 3 8 8 8
vector elements:9 8 7 6 5 4 3 8
4.程序剖析
本实例主要演示了STL算法组件中fill()、reverse()以及unique()三个方法操作应用,三个方法分别表示针对当前序列的填充、颠倒序列元素以及清除序列中相邻的同元素操作。
第一部分代码与前面实例相同首先定义空整型向量对象testVector,以及两个同等类型的迭代器分别表示区间头与尾。采用for循环结构向该容器中填入0~10的整型数据,随后通过迭代器遍历访问该容器序列并打印输出。随后将迭代器定位,firstIter与enditer两个迭代器分别指向容器首部和尾部。调用算法组件中的fill方法,区间参数为firstIter所指位置到往后3个元素的位置处,填充值为8的元素。并且随后打印输出验证结果,容器序列中前3个元素被值为8的元素填充替换,此时的序列为“8 8 8 3 4 5 6 7 8 9”。
第二部分代码重新定位迭代器位置,调用reverse方法颠倒序列中的元素,参数区间为整个容器从首部到尾部之间。此时打印输出验证容器序列中的元素因为调用reverse方法而发生颠倒,序列为“9 8 7 6 5 4 3 8 8 8”。
第三部分代码算法演示则调用unique算法,去除了容器中相邻相同的元素,调用该方法直接传入容器调用begin与end方法返回的迭代器区间参数,表明将在整个容器的首部与尾部来作该操作,打印输出验证,此时序列中的元素为“9 8 7 6 5 4 3 8”,相邻重复的元素8被删除只剩下一个。
16.4.4 排序算法
STL组件针对排序提供了超过20多种之多的算法实现,通常应用程序处理大数据量中排序算法的选择应用也是最普遍的。由于该类算法种类庞杂而多,下面将会通过介绍几种最常用的排序算法操作,从而了解算法组件中排序算法的一般操作规律。
排序算法中比较重要的几类算法包括sort()、stable_sort()以及partial_sort()。其中sort算法原型为sort(firstIter,endIter),主要按照规定排序迭代器firstIter到enditer区间内的元素。stable_sort算法同样拥有sort操作两个参数,但是该算法保持序列中相等元素的相对位置,从而减少因为移动而带来的处理时间消耗。
而partial_sort算法原型为partial_sort(firstIter,midIter,endIter),主要排序的元素区间为firstIter到endIter之间,将排序好的部分元素首先放置到firstIter与midIter之间,其余未经过排序的部分放置到midIter与endIter之间。
下面将通过完整实例演示其中sort()与partial_sort()算法操作的应用情况,根据不同的需求从而选择不同的算法调用,实例中依然采用向量容器作为基本序列操作的对象。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1615.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1615
* 源文件chapter1615.cpp
* 排序算法应用实例
*/
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
//第一部分代码
vector<int>testVector; //定义int整型类型vector容器对象testVector
vector<int>::iteratorfirstIter; //定义int整型类型迭代器firstIter
vector<int>::iteratorendIter; //定义int整型类型迭代器endIter
testVector.push_back(2); //通过push_back()方法将元素2放入容器
testVector.push_back(3); //通过push_back()方法将元素3放入容器
testVector.push_back(1); //通过push_back()方法将元素1放入容器
testVector.push_back(6); //通过push_back()方法将元素6放入容器
testVector.push_back(8); //通过push_back()方法将元素8放入容器
testVector.push_back(10); //通过push_back()方法将元素10放入容器
cout<<"vectorelements:"; //提示输出容器内容
for(firstIter = testVector.begin();firstIter !=testVector.end();firstIter++) //通过循环遍历容器
{
cout<<*firstIter<<""; //输出迭代器指向内容
}
cout<<endl; //第二部分代码
firstIter = testVector.begin(); //通过begin()方法将容器testVector首元素赋值给firstIter指向
endIter = testVector.end(); //通过end ()方法将容器testVector首元素赋值给endIter指向
sort(firstIter,endIter); //通过sort()方法对容器元素进行排序
cout<<"vector elements:"; //提示输出容器内容
for(firstIter = testVector.begin();firstIter !=testVector.end();firstIter++) //通过循环遍历容器
{
cout<<*firstIter<<""; //输出迭代器指向内容
}
cout<<endl;
//第三部分代码
firstIter = testVector.begin(); //通过begin()方法将容器testVector首元素赋值给firstIter指向
endIter = testVector.end(); //通过end ()方法将容器testVector首元素赋值给endIter指向
random_shuffle(firstIter,endIter); //通过random_shuffle方法随机打乱容器内元素
cout<<"vector elements:"; //提示输出容器内容
for(firstIter = testVector.begin();firstIter !=testVector.end();firstIter++) //通过循环遍历容器
{
cout<<*firstIter<<""; //输出迭代器指向内容
}
cout<<endl;
partial_sort(testVector.begin(),testVector.begin()+4,testVector.end());//通过partial_sort方法对容器排序
cout<<"vectorelements:"; //提示输出容器内容
for(firstIter = testVector.begin();firstIter !=testVector.end();firstIter++) //通过循环遍历容器
{
cout<<*firstIter<<""; //输出迭代器指向内容
}
cout<<endl;
return0;
}
2.编辑makefile
Linux平台下需要编译源文件为chapter1615.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1615.o
CC=g++
chapter1615: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1615
clean:
rm-f chapter1615 core $(OBJECTS)
submit:
cp-f -r chapter1615 ../bin
上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c -ochapter1615.o chapter1615.cpp
g++ chapter1615.o -g -o chapter1615
[developer @localhost src]$ make submit
cp -f -r chapter1615../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1615
vector elements:2 3 1 6 8 10
vector elements:1 2 3 6 8 10
vector elements:8 6 2 3 1 10
vector elements:1 2 3 6 8 10
4.程序剖析
本实例主要演示了vector容器中操作排序基本算法的方法,下面将会将代码分为三个部分进行分析。
第一部分代码实例中定义空的整型向量对象testVector,同时定义两个迭代器分别表示容器首尾。随后通过调用其push_back方法,在容器尾部添加不同的元素。此时容器中的元素序列根据迭代器遍历打印输出结果为“2 3 1 6 8 10”。
第二部分代码将两个迭代器分别定位到向量testVector首部与尾部位置,调用sort算法操作排序参数区间的元素,其排序结果打印输出为“1 2 3 6 8 10”。默认情况下sort算法操作按照升序方式排序序列中的元素,当然也可以通过第三个参数来指定排序方式。
第三部分代码首先通过random_shuffle方法将首尾迭代器之间的元素进行随机排列,之后演示partial_sort算法操作,其传入的第一个参数为testVector向量首部元素的迭代器,第二个参数则指定为指向首部元素向后挪4个位置的迭代器表示中间位置,第三个参数则为指向向量尾部的迭代器。跟据该算法操作的基本描述,该方法应该将已经排序的元素放入中间迭代器位置之前,未排序的则放置之后。最后通过迭代器遍历打印输出结果验证,该容器中结果为“1 2 3 6 10 8”,此时从第4个元素位置开始前面都是已经排序的元素,而从第4个元素之后两个元素则未排序。
16.4.5 数值算法
STL标准库算法组件针对数值处理提供了4个算法实现,分别为accumulate()、inner_product()、partial_sum()以及adjacent_difference()。四个算法基本原型与接口说明如表格16.7所示。
表格16.7 STL标准库数值算法接口说明
算法接口 |
说明 |
accumulate(firstIter,endIter,value) |
计算序列中从firstIter到endIter区间内每个元素与value之和 |
inner_product(firstIter,endIter,firstIter1,value) |
计算序列中value与从firstIter到endIter区间内每个元素与firstIter1所指范围内元素乘积之和 |
partial_sum(firstIter,enditer,result) |
计算迭代器firstiter与enditer区间中元素的部分之和,结果放入result中,即该区间的第一个元素作为结果result第一个元素,随后前面相邻两个元素相加作为第二个元素,以此递归下去 |
adjacent_difference(firstIter,endIter,result) |
同上描述,仅仅是计算了元素相邻差而已 |
下面将会通过实例演示4种数值算法在应用程序中的实际使用方式,实例如下所示。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1616.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1616
* 源文件chapter1616.cpp
* 数值算法应用实例
*/
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
int main()
{
//第一部分代码
vector<int>testVector; //定义int整型类型vector容器对象testVector
vector<int>testVector1(6); //定义int整型类型vector容器对象testVector1
vector<int>::iteratorfirstIter; //定义int整型类型迭代器firstIter
vector<int>::iteratorendIter; //定义int整型类型迭代器endIter
vector<int>::iteratortempIter; //定义int整型类型迭代器tempIter
testVector.push_back(2); //通过push_back方法将元素2放入容器testVector
testVector.push_back(3); //通过push_back方法将元素3放入容器testVector
testVector.push_back(1); //通过push_back方法将元素1放入容器testVector
testVector.push_back(6); //通过push_back方法将元素6放入容器testVector
testVector.push_back(8); //通过push_back方法将元素8放入容器testVector
testVector.push_back(10); //通过push_back方法将元素10放入容器testVector
cout<<"vectorelements:"; //提示输出容器内容
for(firstIter= testVector.begin();firstIter != testVector.end();firstIter++) //通过循环遍历容器元素
{
cout<<*firstIter<<""; //输出迭代器指向元素值
}
cout<<endl;
firstIter= testVector.begin(); //通过begin()方法将容器testVector首元素赋值给迭代器firstIter
endIter = testVector.end(); //通过end ()方法将容器testVector首元素赋值给迭代器endIter
intresult; //定义整型结果变量result
result= accumulate(firstIter,endIter,2); //通过accumulate方法计算容器内元素,同时计算元素2和
cout<<"result= "<<result<<endl; //输出该计算结果值
//第二部分代码
firstIter= testVector.begin(); //通过begin()方法将容器testVector首元素赋值给迭代器firstIter
endIter = testVector.end(); //通过end ()方法将容器testVector首元素赋值给迭代器endIter
result= inner_product(firstIter,endIter,firstIter+2,8); //通过inner_product方法做容器元素内乘积计算
cout<<"result= "<<result<<endl; //输出容器乘积结果
//第三部分代码
firstIter= testVector.begin(); //通过begin()方法将容器testVector首元素复制给迭代器firstIter
endIter =testVector.end(); //通过end()方法将容器testVector尾元素复制给迭代器endIter
partial_sum(firstIter,endIter,testVector1.begin()); //通过partial_sum()方法将容器内元素实现迭代和计算
cout<<"vectorelements:"; //提示输出容器结果
for(firstIter= testVector1.begin();firstIter != testVector1.end();firstIter++) //通过循环遍历该容器内部元素
{
cout<<*firstIter<<""; //输出迭代器指向元素值
}
cout<<endl;
//第四部分代码
firstIter= testVector.begin(); //通过begin()方法将容器testVector首元素复制给迭代器firstIter
endIter = testVector.end(); //通过end()方法将容器testVector尾元素复制给迭代器endIter
adjacent_difference(firstIter,endIter,testVector1.begin()); //通过adjacent_difference方法实现容器元素迭减
cout<<"vectorelements:"; //提示输出计算结果
for(firstIter= testVector1.begin();firstIter != testVector1.end();firstIter++) //通过循环遍历该容器内部元素
{
cout<<*firstIter<<""; //输出迭代器指向元素值
}
cout<<endl;
return0;
}
2.编辑makefile
Linux平台下需要编译源文件为chapter1616.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1616.o
CC=g++
chapter1616: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1616
clean:
rm-f chapter1616 core $(OBJECTS)
submit:
cp-f -r chapter1616 ../bin
上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c -ochapter1616.o chapter1616.cpp
g++ chapter1616.o -g -o chapter1616
[developer @localhost src]$ make submit
cp -f -r chapter1616../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1616
vector elements:2 3 1 6 8 10
result = 32
result = 96
vector elements:2 5 6 12 20 30
vector elements:2 1 -2 5 2 2
4.剖析程序
本实例演示了上述4种数值算法操作,分别为accumulate、inner_product、partial_sum和adjacent_difference。程序将会分为四个部分进行剖析讲解。
第一部分代码程序中首先定义两个vector向量对象,一个为空另一个为大小指定6个元素。随后将向量对象testVector调用push_back方法添加元素进入容器中,打印输出该容器中元素为“2 3 1 6 8 10”。
定位两个迭代器对象的位置为testVector容器的首部与尾部,同时定义整型变量result作为调用accumulate算法计算的结果。该算法中以区间firstIter到endIter之间的元素累加并加上第三个元素值之和作为计算结果返回赋给result,打印输出该结果为32,内部实现“2+3+1+6+8+10+2”的计算。
第二部分代码将迭代器重新定位到testVector向量首尾部位置,随后调用inner_product方法根据传入的参数将计算结果赋值给result变量打印输出。由于计算区间为firstiter到enditer之间,同时指定另一个参数为firstIter+2的位置开始往后的元素,两边元素相乘最后相加结果加上第四个元素值8。该算法内部计算为序列一“2 3 1 6 8 10”,序列二“1 6 8 10 0 0”,计算为“2*1+3*6+1*8+6*10+8*0+10*0+8”最后结果为96。
第三部分代码算法操作partial_sum接口的调用中,参数区间为容器对象testVector首尾部迭代器指定的区间,第三个参数即计算结果则存放入另一个容器对象testVector1,所以该参数为指向该容器首部的迭代器。根据该方法算法描述,该操作将testVector中第一个元素作为结果容器中第一个元素,随后的每个元素都为前两个元素之和,即计算过程为第一个元素为“2”,随后“2+3”、“1+2+3”、“6+1+2+3”、“8+6+1+2+3”“10+1+2+3+6+8”,最后计算结果存放入容器testVector1中,打印输出该容器中内容结果为“2 5 6 12 20 30”。
第四部分代码与partial_sum算法计算想反,adjacent_difference算法操作则将相邻两个元素相减的结果放入结果容器中,容器testVector中存放元素为“2 3 1 6 8 10”,调用该方法后计算结果容器中存放的元素为“2”、“3-2”、“1-3”、“6-1”、“8-6”、“10-8”,最后结果容器中打印输出序列为“2 1 -2 5 2 2”。
16.5 小结
STL标准库提供的内容主要分为六个部分,由于篇幅限制本书只向初学者介绍其中最常用的前三个部分。其中函数对象、内存分配器以及配接器随着深入学习模板部分编程时再逐步讲述。STL模板库包含内容非常的丰富,详细讲述需要通过一本书都介绍不完全部内容,本章的宗旨在于通过讲述其中部分常见模板库的应用情况,通过一些操作实例实践希望初学者能够掌握STL库一般使用方法,从而触类旁通进而掌握使用STL库的方法。