C++学习之:STL标准模板库

STL提供了一组表示容器、迭代器、函数对象和算法的模板。
容器是一个与数组类似的单元,可以存储若干个值。STL容器是同质的,即存储的值的类型相同;
算法是完成特点任务(如对数组进行排序,或在链表中查找特定值)。
迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,是广义指针。迭代器使算法独立于使用的容器类型
所有STL容器都提供了一些基本法方法,其中包括

size()---返回容器中元素数目
swap()---交换两个容器的内容
begin()---返回一个指向容器中第一个元素的迭代器
end()---返回一个表示超过容器为的迭代器。

1.容器

(1)vector(在头文件vector中声明)
vector是数组的一种类表示,它提供了自动内存管理功能,可以动态地改变vector对象的长度,并随着元素的添加和删除而增大或缩小。可以实现对对象的随机访问。在尾部添加和删除元素的时间是固定的,但在头部或中间插入和删除元素的复杂度是线性时间。
vector还是可反转容器(reversible container)的概念的模型。增加了两个类方法:rbegin()和rend(),分别返回一个指向反转序列的第一个元素的迭代器,后者返回反转序列的超尾迭代器。比如下面的代码

    vector<int> vec;
    vector<int> ::iterator pr;
    for (int i = 5; i > 0;i--)
    {
        vec.push_back(i);
    }
    for (pr = vec.begin(); pr !=vec.end();pr++)
    {
        cout << *pr << endl;
        //cout << "success!";
    }
    cout << "=============翻转============" << endl;
    vector<int>::reverse_iterator pb;//迭代器类型是reverse_iterator 对这种迭代器递增,将导致它反向遍历可反转容器
    for (pb = vec.rbegin(); pb != vec.rend(); pb++)
    {
        cout << *pb<<endl;
    }

vector容器中可以使用的方法

erase() 删除矢量中给定区间的元素 接受两个参数,这些参数定义要删除的区间
push_back(x) 将元素x添加到容器尾部 vec.push_back(5)
insert() 插入元素到某区域处 接受三个参数

1.erase()函数详解

erase()函数接受两个参数,这两个参数定义要删除元素区间,参数用迭代器类型表示,第一个参数即迭代器指向区间的起始位置,第二个参数即迭代器位于区间终止处的后一个位置

vec.erase(vec.begin(),vec.begin()+2)//删除vec容器中第一个和第二个元素 ,删除区间为[vec.begin(),vec.begi()+2)

2.insert()函数解析

//用法1:在指定位置it前“插入”值为val的元素,返回指向这个元素的迭代器,
iterator insert( iterator it, const TYPE &val );
//用法2:在指定位置it前“插入”num个值为val的元素 
void insert( iterator it, size_type num, const TYPE &val );
//用法3:在指定位置it前“插入”区间[start, end)的所有元素. 
void insert( iterator it, input_iterator start, input_iterator end ); 

举例: 
//创建一个vector,置入字母表的前十个字符 
vector <char> charV; 
for( int i=0; i < 10; i++ ) 
  charV.push_back( i + 65 ); 

//插入四个C到vector中 
vector <char>::iterator it = charV.begin(); 
charV.insert( it, 4, 'C' ); 

//显示vector的内容 
for( it = charV.begin(); it != charV.end(); it++ ) 
  cout < < *it; 

这段代码将显示:

CCCCABCDEFGHIJ

(2)deque(在头文件deque中声明)
deque模板类表示双端队列(double-ended queue),通常简称为deque。在STL中其实现类似于vector容器,支持随机访问。与vector的区别在于:从deque对象的开始位置插入和删除元素的时间是固定的,而vector在开始和中间位置插入和删除元素的时间是线性的。如果多数操作发生在序列的起始位置和结尾处,则应考虑使用deque数据结果。
为实现deque两端执行插入和删除操作的时间为固定的这一目的,deque的设计比vector更复杂。尽管两者都提供对元素的随机访问和在序列中部执行线性时间的插入和删除操作,但vector容器执行这些操作的速度更快些
(3)list(在头文件list中声明)
list模板表示双向链表。除了第一个和最后一个元素外,每个元素都与前后的元素相链接,意味着可以双向遍历链表。
list和vector的区别在于,list在链表中间任意位置进行插入和删除的时间是固定的。而vector则在尾端进行插入和删除的时间是固定的。
vector也是可反转容器。与vector不同的是list不支持数组表示法和随机访问
(4)queue(在头文件queue中声明)
queue模板类是一个适配器类。queue模板让底层类(默认为deque)展示典型的队列接口。queue模板的限制比deque的多。queue不允许随机范围队列元素,不允许遍历队列。它把使用限制在定义队列的基本操作上,可以将元素添加到队尾、从队首删除元素、查看队首和队尾的值、检查元素数目和测试队列是否为空。
所有的STL容器都提供了一些基本法,其中包括以下几种方法(不完全列举)

size() 返回容器中元素数目
swap() 交换两个容器中的内容
begin() 返回一个指向容器第一个元素的迭代器
end() 返回一个表示超过容器尾的迭代器

2.迭代器

STL按照功能的强弱定义了多种级别的迭代器。C++ Prime Plus中有一个例子,分别在数组和链表中实现find()函数

//在数组中实现查找某一个值
double *find_ar(double *ar,int n,const double& val)
{
 for(int i=0;i<n;i++)
  {
     if(ar[i]==val)
      return &ar[i];
     return 0;
  }
}
//在链表中实现查找某一个值
struct Node{
double item;
Node* p_next;
};

假如有一个指向链表第一个节点的指针,每个节点的p_next指针指向下一个节点,链表最后一个节点的p_next指针被设置为0,则可以这样写:

Node* find_ll(Node* head, const double& val)
{
    Node* start;
    for(start=head;start!=0;start=start->p_next)
        {
            if(start->item==val)
            {return start;}
        }
        return 0;
}

从技术实现上看,两个find函数算法是不同的:第一个使用数组索引来遍历元素,第二个将start重置为start->p_next。但是两个函数效果是一样的:将值依次与容器中的每个值进行比较,直到找到匹配的为止。
泛型编程要做的使用同一个find函数来处理数组、链表、或任何其他容器类型。即函数不仅独立于容器中存储的数据类型(int、double、char等)还要独立于容器本身的数据结构(数组、链表等)。其中模板提供了存储在容器中的数据类型的通用表示,因此还需要遍历容器中的值的通用表示,迭代器就是这样的通用表示
迭代器要满足如下要求:

  • 能够对迭代器执行解除引用的操作,以便能够访问它引用的值。即如果p是一个迭代器,则应该对*p进行定义。
  • 应能够将一个迭代器赋给另一个。p=q
  • 能够将迭代器与另一个进行比较,看它们是否相等。p==q,p!=q
  • 能够使用迭代器遍历容器中的所有元素,通过为迭代器p定义++p和p++来实现

首先,每个容器类(vector、list、deque等)定义了相应的迭代器。对应其中的某个类,迭代器可能是指针;而对于另外一个类,则可能是对象。但是所有的迭代器都将提供如上所说的操作。
每个容器类都有一个超尾标记,当迭代器递增到超越容器的最后一个值后,这个值将被赋给迭代器。
每个容器类都有begin()、end()方法,它们分别返回一个指向第一个元素和超尾位置的迭代器。
每个容器类都使用++操作,让迭代器从指向第一个元素逐步指向超尾位置,从而遍历容器中的每一个元素。
注意:使用容器类,无需知道其迭代器是如何实现的,也无需知道超尾是如何实现的,只需知道它有迭代器。
比如:

//vector中的迭代器
vector<double> ::iterator pr;
for(pr=scores.begin();pr!=scores.end();pr++)
cout<<*pr<<endl;
//list中的迭代器
list<double> ::iterator pr;
for(pr=scores.begin();pr!=scores.end();pr++)
cout<<*pr<<endl;

STL通过为每个类定义适当的迭代器,并以统一的风格设计类,能够对内部表示绝然不同的容器,编写同样的代码。

2.1迭代器类型

STL按照功能的强弱定义了5种迭代器。分别是:输入迭代器、输出迭代器、正向迭代器、双向迭代器、和随机访问迭代器。设计这么多种类的迭代器是在编写算法时尽量使用要求最低的迭代器,并让它适用于容器的最大区间。
STL遵循这样的方法:首先,每个容器类(vector、list、deque等)定义了相应的迭代器类型。对应其中的某个类迭代器可能是指针;而对于另外一个类,迭代器则可能是对象。不管实现方式如何不同,迭代器都有相同的操作:如*和++等。
使用容器类,不需要知道其迭代器是如何实现的,也无需知道超尾是如何实现的,只需要知道它有迭代器,其begin()返回指向第一个元素的迭代器,end()返回指向一个指向超尾的迭代器即可。
比如下边的vector和list容器的迭代器看着一样,但是实现方式是不一样的,两者都用嵌套类实现了各自的迭代器类

vector<int> ::iterator pr;
list<int >::iterator pl;

补充:固定时间和线性时间

猜你喜欢

转载自blog.csdn.net/Wu_qz/article/details/80712413