标准模板库的英文缩写是STL,即Standard Template Library。

STL里面有什么呢?

  它提供了一组表示容器、迭代器、函数对象和算法的模板。

  容器是一个与数组类似的单元,可以存储若干值。

  STL容器是同质的,即存储的值的类型相同。

  算法是完成特定任务的处方(例如对数组进行排序或在链表中查找特定的值)。

  迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,属于广义指针。

  函数对象是类似于函数的对象,可以是类对象或函数指针(包括函数名,因为函数名被用作指针)。

 

STL使得能够构造各种容器(数组、队列、链表)和执行各种操作(搜索、排序和随机排列)。

STL是C++标准的一部分。

STL不是面向对象的编程,而是一种不同的编程模式——泛型编程(generic programming)

这使得STL在功能和方法上都很有趣。

这篇文章主要通过介绍例子,来对容器、迭代器和算法有感性的认识;领会泛型编程的精神;介绍底层的设计理念,简要地介绍STL。

==================================

一、模板类vector

在计算机中,矢量对应于数组,而不是数学矢量。

在数学中,可以使用N个分量来表示N维数学矢量

因此从这方面讲,数学矢量类似于一个N维数组。

当然,数学矢量还有一些计算机矢量不具备的特征,例如内乘积和外乘积。

接下来看一下计算矢量有哪些特征:

1、存储了一组可随机访问的值;

2、可以使用索引来直接访问矢量的第n个元素;

3、可以创建一个vector对象,将对象赋给另一个对象,使用[ ]运算符来访问vector元素。

要使类成为通用的,应将它设计为模板类。

STL正是这样做的——在头文件vector中定义了一个vector模板。

要创建vector模板对象,可使用通常的<type>表示法来指出要使用的类型

另外,vector模板使用动态内存分配,因此可以用初始化参数来指出需要多少矢量:

#include vector

using namespace std;

vector <int> rating(5);   //a vector of 5 ints

int n;

cin >> n;

vector<double> scores(n);  //a vector of n doubles

由于运算符[ ]被重载,因此创建vector对象后,可以使用通常的数组表示法来访问各个元素:

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。

接下来有个示例程序,它使用了这个类。该程序创建了vector对象,一个是int规范、一个是string规范,它们都包含5个元素。

 // vect1.cpp  -- introducing the vector template
#include <iostream>
#include <string>
#include <vector> const int NUM = ;
int main()
{
using std::vector;
using std::string;
using std::cin;
using std::cout; vector<int> ratings(NUM);
vector<string> titles(NUM);
cout<<"You will do exactly as told.Your will enter\n"<<NUM<<" book titles and your ratings (0-10).\n";
int i;
for (i=; i<NUM;i++)
{
cout << "Enter title #"<<i+<<": ";
getline(cin,titles[i]);
cout<<"Enter your rating (0-10):";
cin >>ratings[i];
cin.get();
}
cout<<"Thank you. You entered the following:\n"<<"Rating\t\Book\n";
for (i=; i<NUM; i++)
{
cout<<ratings[i]<<"\t"<<titles[i]<<endl;
}
return ;
}

==================================

二、可对矢量执行的操作

除了分配空间外,还可以对模板完成一些基本的操作;

STL容器都提供了一些基本方法,其中包括size()——返回容器中元素数目、swap()——交换两个容器的内容、begin()——返回一个指向容器中第一个元素的迭代器、end()——返回一个表示超过容器尾的迭代器。

那么什么是迭代器呢?它实际上是一个广义指针。它可以是指针,也可以是一个可对其执行类似指针的操作——如解除引用和递增。

通过将指针广义化为迭代器,让STL能够为各种不同的容器类提供统一的接口。

该迭代器的类型是一个名为iterator的typedef,其作用域为整个类。

要为vector的double类型规范声明一个迭代器,可以这样做:

vector<double>::iterator pd;   //pd an iterator

假设scores是一个vector<double>类型的对象;

vector<double> scores;

则可以使用迭代器pd执行这样的操作:

pd = scores.begin();

*pd = 22.3;

++pd;

可以看到迭代器的行为像一个指针;

另外C++11还有一个自动类型推断的功能如下:

auto pd = scores.begin();

接下来讨论什么是超过结尾(past-to-end)?它是一种迭代器,指向容器最后一个元素后面的一个元素。

这与C风格字符串的最后一个字符空字符类似,空字符是一个值。

而“超过结尾”是一个指向元素的指针(迭代器)。

end()成员函数标识超过结尾的位置。

如果将迭代器设置为容器的第一个元素,并不断地递增,最终它将到达容器的末尾,从而遍历整个容器的内容。

则可以使用下列代码来显示容器的内容:

for(pd = scores.begin(); pd != scores.end(); pd++)

  cout<<*pd<< endl;

另外push-back()是一个方便的方法,它将元素添加到容器的末尾。这样做的时候,它将负责管理内存,增加矢量的长度,使之能容纳新的成员。

可以这样写代码:

vector<double> scores;

double temp;

while(cin>>temp && temp>=0)

  socres.push_back(temp);

cout<<"Your entered"<<scores.size()<<"scores.\n";

每次循环都给对象添加元素,无需了解元素的数目,只要有内存,就能添加元素。

earse()方法删除容器中指定区间的元素。它接受两个迭代器参数,这些参数定义了要删除的区间。

了解STL如何使用两个迭代器来定义区间非常重要。

第一个迭代器指向区间的起始位置,第二个迭代器指向位于区间终止处的后一个位置。

代码如下,该代码表示删除begin()和begin()+1指向的元素:

scores.erase(scores.begin(), scores.begin()+2);

我们发现STL文档中会使用[ )方法来表示区间。注意,这种不是C++的标准,仅用于文档表达;

[p1, p2)用来表示p1到p2(不包括p2)的区间。这是一种前闭后开的区间。STL容器就是根据这个约定来定义区间范围的。

[begin(), end()]表示集合的所有内容。

insert()方法的功能和erase()相反。它接受3个迭代器参数,第一个参数指定了新元素的插入位置,第二个和第三个参数定义了要插入的属于另一个对象的新元素区间,该区间通常是另一个容器对象的一部分。

代码如下:

vector<int> old_v;

vector<int> new_v;

...

old_v.insert(old_v.begin(), new_v.begin()+1, new_v.end());

上述代码表示将矢量new_v中的除第一个元素以外的所有元素插入到old_v矢量的第一个元素前面;

另外,对于上面这种情况来说,拥有超尾元素(past-to-end)带来了方便。因为这使得在矢量尾部添加元素非常简单。

old_v.insert(old_v.end(), new_v.begin()+1, new_v.end());

接下来有个示例程序,演示了各个容器方法的使用。

 //vect2.cpp  -- methods and iterators
#include <iostream>
#include <string>
#include <vector> struct Review {
std::string title;
int rating;
}; bool FillReview(Review & rr);
void ShowReivew(const Review & rr); int main()
{
using std::cout;
using std::vector;
vector<Review> books;
Review temp;
while (FillReview(temp))
book.push_back(temp);
int num = books.size();
if (num >)
{
cout<<"Thank you. Your entered the following:\n"
<<"Rating\tBook\n";
for (int i =; i<num; i++)
ShowReview(books[i]);
cout<<"Reprising:\n"<<"Rating\tBooks\n";
vector<Review>::iterator pr;
for (pr = books.begin(); pr != books.end(); pr++)
ShowReview(*pr);
vector<Review>oldlist(books);
if (num >)
{
//remove 2 items
books.erase(begin.begin()+, books.begin()+)
cout<< "After erasure:\n";
for (pr = books.begin(); pr != books.end(); pr++)
ShowReview(*pr); //insert 1 item
books.insert();
cout<<"After insertion:\n";
for (pr =books.begin(); pr != books.end(); pr++)
ShowReview(*pr);
}
books.swap(oldlist);
cout<<"Swapping oldlist with books:\n";
for(pr = books.begin(); pr != books.end();pr++)
ShowReview(*pr);
}
else
cout<<"Nothing entered, nothing gained.\n";
return ;
} bool FillReview(Review & rr)
{
std:;cout<<"Enter book title(quit to quit):";
std::getline(std::cin, rr.title);
if (rr.title == "quit")
return false;
std::cout<<"Enter book rating: ";
std::cin >> rr.rating;
if(!std::cin)
return false;
while(std::cin.get() != '\n')
continue;
return true;
} void ShowReview()
{
std::cout<<rr.rating<<"\t"<<rr.title<<std::endl;
}

==================================

三、对矢量可执行的其他操作

程序员通常要对数组执行很多操作,如搜索、排序、随机排序等;

矢量模板包含了执行这些常见的操作方法吗?没有!

STL从更广泛的角度定义了非成员函数来执行这些操作,既不是为每个容器类定义find()成员函数,而是定义了一个适用于所有容器类的非成员函数find()。

这种设计理念省去了大量的重复工作。

举个例子,假设有8个容器类,需要支持10种操作。如果每个类都有自己的成员函数,则需要定义80个成员函数。

但是采用STL的方法,只需要定义10个非成员函数即可。

在定义新的容器类时,只要遵循正确的指导思想,则它也可以使用已有的10个非成员函数来执行查找、排序等操作。

当然有时候,即使有执行相同任务的非成员函数,STL有时也会定义一个成员函数。这是因为对有些操作来讲使用类的特定算法的效率比通用算法高。

因此,vector的成员函数swap()的效率比非成员函数swap()高,但非成员函数让您能够交换两个类型不同的容器的内容。

接下来讨论3个具有代表性的STL非成员函数:for_each()、random_shuffle()和sort()。

for_each()函数可用于很多函数类,它接受3个参数。前两个是迭代器用于定义容器中的区间,最后一个是指向函数的指针(更普遍地说,最后一个参数是一个函数对象,函数对象将稍后介绍)。

可以将代码:

vector<Review>::iterator pr;

for (pr = books.begin(); pr != books.end(); pr++)

  ShowReview(*pr);

替换为:

for_each(books.begin(),books.end(), ShowReview);

这样可以避免显式地使用迭代器变量。

Random_shuffle()函数接受两个指定区间的迭代器参数,并随机排列该区间中的元素。

例如,下面的语句随机排列books矢量中的所有元素:

random_shuffle(books.begin(), books.end());

sort()函数也要求容器支持随机访问。该函数有两个版本。

第一个版本接受两个定义区间的迭代器参数,并使用为存储在容器中的类型元素定义的<运算符,以对区间中的元素进行操作。

下面的语句按照升序对coolstuff的内容进行排序,排序时使用内置的<运算符对值进行比较:

vector<int> coolstuff;

...

sort(coolstuff.begin(), coolstuff.end());

如果容器元素是用户定义的对象,则要使用sort(),必须定义能够处理该类型对象的operator<()函数。

例如,如果为Review提供了成员函数或非成员函数operator<(),则可以对包含Review对象的矢量进行排序。

由于Review是一个结构,因此其成员是公有的,这样的非成员函数将为:

bool operator<(const Review & r1, const Review & r2)

{

  if(r1.title <r2.title)

    return true;

  else if (r1.title == r2.title && r1.rating<r2.rating)

    return true;

  else

    return false;

}

有了这样的函数,就可以对包含Review对象的矢量进行排序了;

sort(books.begin(), books.end());    //这两个参数是指定容器区间的迭代器;

上述的排序是按照升序的方式进行,使用内置的<运算符进行排序;

如果想要按照降序的方式的话,可以自己定义一个函数WorseThan();

有了这个函数后,就可以对包含Review对象的book矢量进行降序排序:

sort(books.begin(), books.end(),WorseThan());

接下来这段程序将演示STL非成员函数的用法:

 //vect3.cpp -- using STL functions
#include <iostream>
#include <string>
#include <vector>
#include <algorithm> struct Review {
std::string title;
int rating;
} bool operator<(const Review & r1, const Review & r2);
bool worsethan(const Review & r1, const Review & r2);
bool FillReview(Review & rr);
void ShowReview(const Review & rr); int main()
{
using namespace std; vector<Review> books;
Review temp;
while(FillReview(temp))
books.push_back(temp); if(books.size()>)
{
cout<<""<<books.size()<<""<<"";
for_each(books.begin(), books.end(), ShowReview); sort(books.begin(), books.end());
cout<<"";
for_each(books.begin(), books.end(), ShowReview); sort(books.begin(), books.end(),worseThan);
cout<<"";
for_each(books.begin(), books.end(), ShowReview); random_shuffle(books.begin(), books.end());
cout<<"";
for_each(books.begin(), books.end(), ShowReview);
}
else
{
cout<<"No entries";
}
cout<<"Bye.\n";
return ;
} bool operator<(const Review & r1, const Review & r2)
{
if(r1.title < r2.title)
return true;
else(r1.title == r2.title && r1.rating <r2.rating)
return true;
else
return false;
} bool worsethan(const Review & r1, const Review & r2)
{
if(r1.rating < r2.rating)
return true;
else
return false;
} bool FillReview(Review & rr)
{
std::cout<<"Enter book title(quit to quit)";
std::getline(std::cin, rr.title);
if(rr.title == "quit")
return false;
std::cout<<"Enter book rating:";
std::cin>>rr.rating;
if(!std::cin)
return false;
//get rid of rest of input line
while(std::cing.get() != "\n")
continue;
return true;
} void ShowReview(const Review & rr)
{
std::cout<<rr.rating<<"\t"<<rr.title<<std::endl;
}

==================================

四、基于范围的for循环(C++11)

接下来介绍一种新型的for循环方式,叫做基于范围的for循环;

这种for循环其实是为用于STL而设计的;

下面有个示例:

double price[5] = {4.99, 10.99, 6.87, 7.99, 8.49};

for (double x : prices)

  cout<<x<<std::endl;

首先在for循环的括号中,声明一个变量,该变量的类型与容器中存储的内容相同。然后指出容器的名称。

接下来,循环体使用指定的变量依次访问容器的每个元素。

由此我们可以改写代码:

将代码:for_each(books.begin(), books.end(), ShowReview);

改写成:for(auto x : books) ShowReview(x);

注意这里修饰x变量的关键字是auto。自动变量,这是因为根据books的类型vector<Review>,编译器可以推断出x的类型为Review,而循环将依次将books中的每个Review对象传递给ShowReview()。

这个是自动类型推断功能。

基于范围的for循环还可以修改容器的内容,诀窍是使用一个引用参数。

void InflateReview(Review &r){r.rating++}

接下来:

for(auto x:books) InflateReview(x);

C++_标准模板库STL概念介绍1-建立感性认知的更多相关文章

  1. C++_标准模板库STL概念介绍5-其他库与总结

    C++还提供了其他一些类库,这些类库更加专用. 例如,头文件complex为复数提供了类模板complex,包含用于float.long和long double的具体化. 这个类提供了标准的复数运算以 ...

  2. C++_标准模板库STL概念介绍4-算法

    STL包含很多处理容器的非成员函数: sort() copy() find() random_shuffle() set_union() set_intersection() set_differen ...

  3. C++_标准模板库STL概念介绍2-泛型编程

    有了之前使用STL的经验后,接下来讨论泛型编程及其底层的理念: 首先我们知道STL只是泛型编程的一种: 而面向对象的编程方式关注的是编程的数据方面: 而泛型编程关注的是算法: 但是,他们之间的一个重要 ...

  4. C++_标准模板库STL概念介绍3-函数对象

    函数对象也叫做函数符(functor). 函数符是可以以函数方式和( )结合使用的任意对象. 包括函数名,指向函数的指针,重载了()运算符的类对象. 可以这样定义一个类: class Linear { ...

  5. STL学习系列之一——标准模板库STL介绍

    库是一系列程序组件的集合,他们可以在不同的程序中重复使用.C++语言按照传统的习惯,提供了由各种各样的函数组成的库,用于完成诸如输入/输出.数学计算等功能. 1. STL介绍 标准模板库STL是当今每 ...

  6. C++ 标准模板库STL 队列 queue 使用方法与应用介绍

    C++ 标准模板库STL 队列 queue 使用方法与应用介绍 queue queue模板类的定义在<queue>头文件中. 与stack模板类很相似,queue模板类也需要两个模板参数, ...

  7. 标准模板库--STL

    标准模板库STL 1.泛型程序设计 C++ 语言的核心优势之一就是便于软件的重用 C++中有两个方面体现重用: 1.面向对象的思想:继承和多态,标准类库 2.泛型程序设计(generic progra ...

  8. 实验8 标准模板库STL

    一.实验目的与要求: 了解标准模板库STL中的容器.迭代器.函数对象和算法等基本概念. 掌握STL,并能应用STL解决实际问题. 二.实验过程: 完成实验8标准模板库STL中练习题,见:http:// ...

  9. cb22a_c++_标准模板库_STL_map_multimap红黑树(数据结构)关联容器

    cb22a_c++_标准模板库_STL_map_multimap红黑树(数据结构)关联容器map(映射,key不能重复,一对一对的,value_type(1, "one")),mu ...

随机推荐

  1. zookeeper集群安装的奇怪现象

    zookeeper:配置的集群信息是domain:端口2888:端口3888:  domain为内网静态ip:每次启动都不能相互连接报错误: [myid:3] - WARN  [WorkerSende ...

  2. 如何在局域网架设FTP(特别简单方便)

    https://files.cnblogs.com/files/wlphp/FTPserver.zip 在我上传的博客园文件下载下来 启动服务,设置账号密码(注意一定要关闭防火墙)

  3. Android 创建项目出现No resource found that matches the given name Theme.AppCompat.Light

    关于为何出现No resource found that matches the given name ‘Theme.AppCompat.Light’的原因 这边博客已经写的很清楚了 大家可以参考一下 ...

  4. zend studio 字体大小修改,默认编码设置

    zend studio的字体感觉很小,很多用户不是很适应,修改方法如下: 第一步:进入设置窗口    windows -> preferences 第二步:进入修改字体的选项卡.    Gene ...

  5. c# 如何制作RealPlayer 视频播放器

      c# 如何制作RealPlayer 视频播放器 主要介绍了如何使用 RealPlayer G2 Control 控件 那么我们怎么获得到这个控件呢,很简单,操作方法如下 右单击工具箱对话框的[所有 ...

  6. 2014年Linux 和开源技术回顾盘点

    ZDNet科技观察家StevenJ.Vaughan-Nichols在年终发表了对Linux和开源技术这一年跌宕起伏的总结,细数这一年中的惊喜和不堪. 2014Linux之殇 “心脏出血(Heartbl ...

  7. Linux下面rpm命令和mount命令详解

    在Linux下面我们经常会安装一些软件包,还有挂载命令.接下来,我们通过一些实例来演示这些命令的使用.. 第一步:我们先在linux下面挂载光盘,先进入到根目录,然后切换到根下面的/mnt目录,因为/ ...

  8. (回溯法)ip地址的合理性

    题目: 给定一个只包含数字的字符串,通过返回所有可能有效的IP地址组合来恢复它. 例如: 给定“”, return [“255.255.11.135”,“255.255.111.35”]. (顺序无所 ...

  9. Linq学习<三> linq to entity

    之前一直用sql选择出数据放在一个集合中,然后再用Linq或者lambda去操作数据,今天学了Linq to entity 才知道原来linq产生是为了Entity.也就是EDM(实体数据模型) 关于 ...

  10. Ubuntu普通用户使用串口设备

    将普通用户加入dialout组,然后重启或注销登录 sudo gpasswd --add username dialout