基础备忘:STL基本范例

来源:互联网 发布:mac high sierra 更新 编辑:程序博客网 时间:2024/05/22 10:19


C++标准库与STL的关系

1.STL入门

STL的代码从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),几乎所有的代码都采用了模板类和模版函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。

C++标准中,STL被组织为下面的13个头文件<algorithm><deque><functional><iterator><vector>、<list>、<map>、

<memory><numeric><queue><set><stack><utility>

例1

#include<vector>   //包含头文件 #include<iostream>using namespace std;int main(){    vector<double> a;  //定义数据变量     vector<double>::const_iterator i; //定义变量     a.push_back(1);                     //调用STL中函数     a.push_back(2);    a.push_back(3);    a.push_back(4);    a.push_back(5);    for(i=a.begin();i!=a.end();i++)//循环输出     {        cout<<(*i)<<endl;    }    system("pause");}

结果:

上述代码中,使用了push_back()、begin()和end()函数,而这些函数在程序中并没有被定义过,却可以使用,这是因为这些函数已经被头文件vector.h所包含,如果strcpy()被string.h包含一样。

例2.

#include<vector>#include<cstdlib>#include<algorithm>#include<iostream>using namespace std;template<class T>struct Print{       void operator()(T& x) const//const说明此成员函数不修改类的数据成员       {            if(0==x%2)            {               cout<<x<<' ';                      }            }        };       int main(int argc, char *argv[]){    vector<int> vcInts;    for(int i=0;i<10;i++)    {       vcInts.push_back(i);     }    vector<int>::iterator iterBegin,iterEnd;//定义两个迭代器,分别用来撒向容器的开始和结尾    iterBegin = vcInts.begin();    iterEnd = vcInts.end();    cout<<"输出所有元素: "<<endl;    for(;iterBegin!=iterEnd;++iterBegin)//遍历     {cout<<*iterBegin<<" ";}//输出     cout<<endl;    cout<<"输出偶数元素: "<<endl;    iterBegin=vcInts.begin();//重定向开始位置     for_each(iterBegin,iterEnd,Print<int>());//通过算法for_each和函数对象Print输出容器中的元素     cout<<endl;    system("pause");     return 0;    }
结果
上述代码重载了括号运算符"()",所以可以当做一个函数对象使用。两个迭代器iterBegin和iterEnd分别指向容器的开始和结尾。可以利用这两个迭代器遍历整个容器。最后调用了for_each算法,并结合使用函数对象Print。for_each的行为类似for循环。

2.算法

STL中的算法部分主要由头文件<algorithm>、<numeric>和<functional>组成。例:

#include<iostream>#include<algorithm>#include<functional>#include<vector>using namespace std;class myclass{      public:             myclass(int a,int b):first(a),second(b){}             int first;             int second;             bool operator < (const myclass &m)const //重载运算符<              {                  return first < m.first;                  }           }; bool less_second(const myclass &m1,const myclass &m2) //根据第2个元素比大小 {     return m1.second<m2.second;     }int main(){    int i=0;    vector<myclass> vct;    for(i=0;i<10;i++)    {            myclass my(10-i,i*3);            vct.push_back(my);    }    for(i=0;i<vct.size();i++)        cout<<"("<<vct[i].first<<","<<vct[i].second<<")"<<endl;                     sort(vct.begin(),vct.end()); //按第1个值排序     cout<<"after sorted by the  first: "<<endl;    for(i=0;i<vct.size();i++)        cout<<"("<<vct[i].first<<","<<vct[i].second<<")"<<endl;//输出按第1个值排序结果     sort(vct.begin(),vct.end(),less_second);//按第2个值排序     cout<<"after sorted by the  second: "<<endl;    for(i=0;i<vct.size();i++)        cout<<"("<<vct[i].first<<","<<vct[i].second<<")"<<endl; //输出按第2个值排序结果       system("pause");               return 0;}

结果

3.容器

容器部分主要由头文件<vector>、<list>、<deque>、<set>、<map>、<stack>和<queue>组成。

3.1容器——向量(vector)

例1

#include<iostream>#include<vector>using namespace std;char* szHW = "Hello World";int main(int argc,char* argv[]){    vector <char> vct;//声明一个字符向量 vector     vector <char> :: iterator vi;//为字符数组字义一个游标iterator     char* cptr = szHW;    while(*cptr!='\0')//初始化字符向量,对整个字符串进行循环,用来把数据填放到字符向量中,直到遇到'\0'时结束     {vct.push_back(*cptr);cptr++;}//push_back将数据放在向量的尾部     for(vi=vct.begin();vi!=vct.end();vi++)//将向量中的字符一个个地显示在控制台,这是STL循环的规范化开始,通常是"!=",而不是"<",因为"<"在一些容器中没有定义     cout<<*vi;//使用运算符"*"将数据从游标指针中提取出来     cout<<endl;    system("pause");    return 0;    }

上例中使用了向量<vector>容器类。STL容器中大约有一半是基于向量的。事实上,vector是一种动态数组,是基本数组的类模板。其内部定义了很多基本操作。既然这是一个类,那么它就会有自己的构造函数。预编译头文件为#include<vector>。

例2.也可以像数组一样访问vector元素,如下:

#include<vector>#include<iostream>using namespace std;int main(){    vector<int> a;    for(int i=0;i<10;i++)//这里的i是int型数据,不是迭代器      a.push_back(i*i);    for(int i=0;i<a.size();i++)//用函数size()取容器a的大小      cout<<a[i]<<" ";//用数组方式访问vector元素     cout<<endl;    system("pause");    return 0;    }

例3 在容器中查找特定值的元素,并显示下标位置

#include<vector>#include<string>#include<iostream>using namespace std;int main(){    vector<int> a(10);    vector<int>::iterator i;    a[5]=100;//把a[5]设为100     for(i=a.begin();i!=a.end();i++)      cout<<*i<<" ";     cout<<endl;    vector<int>::iterator intIter=find(a.begin(),a.end(),100);//查找值为100的迭代器     if(intIter!=a.end())//end 返回的 不是最后一个位置的元素的指针。是最后一个元素指针+1     {     cout<<"找到:"<<*intIter<<endl;     cout<<"位置是:"<<intIter-a.begin()<<endl; // 下标位置:迭代器当前位置前去初始位置。      }    else     cout<<"没找到"<<endl;     system("pause");    return 0;    }


vector中定义了以下4种构造函数

1)默认构造函数

构造一个初始长度为0的空向量,其调用格式如:

vector<int> v1;

2)带有单位整形参数的构造函数

此参数描述了向量的初始大小,该构造函数还有一个可选的参数,这是一个类型为T的实例,描述了各个向量中各成员的初始值,其调用如(此时的T为int):

vector<int> v2(init_size,0);//要事先定义init_size,表示向量的初始大小,其成员都被初始化为0

3)带有两个常量参数的构造函数

产生初始值为一个区间的向量。区间由一个半开区间[first,last)来指定,其调用格式如下:

vector<int> v3(firs,last);

注意:stl体系中,所有用到一对迭代器的,都是左闭右开,[ begin(), end() )。end 返回的不是最后一个位置的元素的指针,是最后一个元素指针+1。

4)复制构造函数

构造一个新的向量,作为已存在的向量的完全复制,其调用如下:

vector<int> v4(v3);

此外,在实际程序中使用较多的是向量类的成员函数,其常用的成员函数如下。

begin()、end()、push_back()、insert()、assign()、front()、back()、erase()、empty()、at()、size()。

3.2容器——列表 

列表也是容器类的一种,其所控制的长度为N的序列是以一个有着N个节点的双向链表来存储的,支持双向迭代器,其预编译头文件为#include<list>

模板list的另一个特点是在异常处理方面,对任何容器来说,容器成员函数在执行中抛出的异常,使容器本身处于一种一致状态,可以被销毁,并且容器没有对所分配的存储空间失去控制。但对大部分操作尤其是那些能够影响多个元素的操作,当抛出异常时,并没有制定容器的精确状态,但list保证对于大部分成员函数抛出异常时,容器能够恢复到操作前的状态。列表类的定义如下:

list<T,allocator<T> > ls;//使用默认模板参数,可以省略第2个参数。

成员函数如下:resize()  clear()  front()  back()  push_back()  push_front()  pop_back()  pop_front()  assign()  insert()  erase()  splice()  remove()  remove_if()  unique()  sort()  merge()  reverse()。

#include<list>#include<iostream>int main(){    using namespace std;    list<int> c1;    list<int>:: iterator c1_iter;    c1.push_back(20);    c1.push_back(10);    c1.push_back(30);    cout<<"Before sorting c1 = ";    for(c1_iter = c1.begin();c1_iter!=c1.end();c1_iter++)//输出列表内容                 cout<<" "<<*c1_iter;    cout<<endl;    c1.sort();//调用列表排序函数     cout<<"After sorting c1 = ";    for(c1_iter = c1.begin();c1_iter!=c1.end();c1_iter++)//输出排序后内容                 cout<<" "<<*c1_iter;    cout<<endl;    c1.sort(greater<int>());//调用降序排序函数     cout<<"After sorting with 'greater than' operation, c1 = ";    for(c1_iter = c1.begin();c1_iter!=c1.end();c1_iter++)//输出降序排列后内容                 cout<<" "<<*c1_iter;    cout<<endl;    system("pause");    return 0;            } 
结果

3.3容器——集合

集合(set)也是容器的一种,它的特点在集合中的元素值是唯一的。在集合中,所有成员都是排列好的。如果先后往一个集合中插入:4,3,2,5,则输出该集合时为:2,3,4,5。

#include<iostream>#include<set>#include<string>using namespace std;int main(){    set <string> strset;    set <string> ::iterator si;    strset.insert("Apple");    strset.insert("Bnana");    strset.insert("Orange");    strset.insert("Plate");    strset.insert("Grapes");    strset.insert("Grapes");    for(si=strset.begin();si!=strset.end();si++)       cout<<*si<<" ";    cout<<endl;    system("pause");    return 0;    }

上例中,Grapes虽然插入了两次,但同样的元素只出现一次。

3.4容器——双端队列

双端队列是一个queue容器类(即队列容器),其定义在头文件deque中(即double queue)。在使用该容器时,需在头文件中加上如下语句:#include<deque>

与vector相同点:支持随机访问和快速插入删除,它在窗口中某一位置上的操作所花费的是线性时间。

与vector不同点:deque还支持从开始端插入数据,因为其包含在开始端插入数据的函数push_front()。此外deque也不支持与vector类似的capacity(),reserve()类似的操作。例1:

#include <iostream>#include <deque>using namespace std;typedef deque<int> INTDEQUE;void put_deque(INTDEQUE deque,char* name)//从前向后显示deque的全部元素 {     INTDEQUE::iterator pdeque;     cout <<"The contents of "<<name<<" : ";     for(pdeque=deque.begin();pdeque!=deque.end();pdeque++)        cout<<*pdeque<<" ";     cout<<endl;     }int main(){    INTDEQUE deq1;    INTDEQUE deq2(10,6);    put_deque(deq1,"deq1");    put_deque(deq2,"deq2");        deq1.push_back(2);//在deq1序列后面添加元素     deq1.push_back(4);    cout<<"deq1.push_back(2) and deq1.push_back(4) : "<<endl;    put_deque(deq1,"deq1");        deq1.push_front(5);//在deq1序列前面添加元素     deq1.push_front(7);    cout<<"deq1.push_front(5) and deq1.push_front(7) : "<<endl;    put_deque(deq1,"deq1");        deq1.insert(deq1.begin()+1,3,9);//在deq1中间插入数据     cout<<"deq1.insert(deq1.begin()+1,3,9): "<<endl;    put_deque(deq1,"deq1");        cout<<"deq1.at(4) = "<<deq1.at(4)<<endl;//测试引用类函数     cout<<"deq1[4] = "<<deq1[4]<<endl;        deq1.at(1)=10;    deq1[2]=20;    cout<<"deq1.at(1)=10 and deq1[2]=20 :"<<endl;    put_deque(deq1,"deq1");        deq1.pop_front();//从deq1序列的前后各移去一个元素    deq1.pop_back();    cout<<"deq1.pop_front() and deq1.pop_back(): "<<endl;    put_deque(deq1,"deq1");        deq1.erase(deq1.begin()+1);//清除deq1中的第2个元素     cout<<"deq1.erase(deq1.begin()+1) : "<<endl;    put_deque(deq1,"deq1");        deq2.assign(8,1);//对deq2赋值并显示    cout<<"deq2.assign(8,1) : "<<endl;    put_deque(deq2,"deq2");        system("pause");    return 0;      }

例2

#include<deque>#include<string>#include<iostream>using namespace std;int main(){    deque<string> a;    string word;    for(int i=0;i<5;i++)    {     cin>>word;     a.push_back(word);     }     a.pop_front();//从前面删除一个元素      a.pop_back();//从后面删除一个元素     for(int i=0;i<a.size();i++)//用函数size()取deque容器a的大小      cout<<a[i]<<" ";//用数组方式访问deque元素     cout<<endl;    system("pause");    return 0;    }


3.5容器——栈

容器栈(stack)是一种特殊的容器,其特征是后进先出。支持以下5种操作:

empty():如果栈空,返回true,否则返回false。

size():返回栈中元素的个数。

pop():删除,但不返回栈顶元素。

top():返回,但不删除栈顶元素。

push():放入新的栈顶元素。

#include<stack> #include<iostream>using namespace std;int main(){    const int ia_size = 10;    int ia[ia_size]={0,1,2,3,4,5,6,7,8,9};    int ix = 0;    stack<int> intStack;    for(;ix<ia_size;++ix)     intStack.push(ia[ix]); //压栈     if(intStack.size()!= ia_size)    {cout<<"ERROR"<<endl;return -1;}    int value;    while(!intStack.empty())    {     value = intStack.top();//取栈顶元素      cout<<value<<endl;     intStack.pop();//删除栈顶元素        }    system("pause");    return 0;}    

3.6容器——映射和多重映射

映射和多重映射用于对数据进行快速和高效的检索。同样地,在程序中使用映射和多重映射容器需添加如下头文件:#include<map>

映射map支持下标运算符operator[],可以用访问普通数据的方式访问map,或下标为map的键。而在mutimap中一个键可以对应多个不同的值。

#include<map>#include<iostream>using namespace std;int main(){ map<char,int,less<char> > map1; map<char,int,less<char> >::iterator mapIter; map1['c']=3;//初始化,与数组类似。也可以用 map1.insert(map<char,int,less<char> >::value_type('c',3)); map1['d']=4; map1['a']=1; map1['b']=2; for(mapIter = map1.begin();mapIter!=map1.end();mapIter++)   cout<<(*mapIter).first<<":"<<(*mapIter).second<<" "; cout<<endl; map<char,int,less<char> >::const_iterator ptr; ptr=map1.find('d');//检索对应于d键的值  cout<<(*ptr).first<<"键对应于值: "<<(*ptr).second<<endl; system("pause"); return 0;}

4.迭代器

迭代器实际上是一种泛化指代指针,如果一个迭代器指向容器中的某个成员,那么迭代器将可以通过自增 自减来遍历容器中的所有成员。迭代器是联系容器和算法的媒介,是算法操作容器的接口。

STL中的迭代器主要是由头文件<utility>、<iterator>、<memory>组成。

<utility>包括了贯穿使用在STL中的几个模板声明。

<iterator>头文件中提供了迭代器使用的许多方法。

<memory>头文件中的主要部分是模板类allocator,它负责产生所有容器中的默认分配器。

例如:下面程序实现使用输入/输出迭代器完成了将一个文件里的内容输出到屏幕的功能。

#include<iostream>#include<fstream>#include<iterator>#include<vector>#include<string>using namespace std;int main(){ vector<string> v1; ifstream file("test.txt"); if(file.fail()) {cout<<"打开文件失败,请确定要打开的文件存在"<<endl;} copy(istream_iterator<string>(file),istream_iterator<string>(),inserter(v1,v1.begin()));//如果没有#include<iterator>这里会报错  copy(v1.begin(),v1.end(),ostream_iterator<string>(cout," ")); cout<<endl; system("pause"); return 0;} 

上述代码中,用到了输入迭代器istream_iterator,输出迭代器ostream_iterator。程序完成了将一个文件输出到屏幕的功能,先将文件读入,然后通过迭代器把文件内容复制到类型为字符串的向量容器内,最后输出迭代器输出。inserter是一个输入迭代器函数(迭代器适配器),其使用方法是:

inserter(container ,pos);

综合范例:下面程序救出范围在2~N中的所有素数,其中N在程序运行时由键盘输入,实现代码如下。

#include<iostream>#include<iomanip>//IO控制流头文件 #include<vector>using namespace std;int main(){    vector<int> A(10);    int n;    int primecount = 0,i,j;    cout<<"Enter a value > 2 as upper limit: ";    cin>>n;    A[primecount++]=2;    for(i=3;i<n;i++)    {     if(primecount == A.size())//达到向量大小         A.resize(primecount+10);//调用函数重新设置向量大小      if(i%2 == 0)//不是素数         continue;    j=3;    while(j<=i/2&&i%j!=0)//循环判断是否为素数       j+=2;//变量递加2     if(j>i/2)//是素数       A[primecount++] = i;//写入向量     }    for(i=0;i<primecount;i++)    {     cout<<setw(5)<<A[i];//设置输出格式并输出      if(0==(i+1)%10)//第10行输出一个换行         cout<<endl;    }    cout<<endl;    system("pause");    return 0;}

上述代码中,使用了向量容器,用于存储素数。该程序采用循环取余的方法求出范围在2~N中的所有素数。