标准模板库的英文缩写是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. 第01章 开发准备(对最新版的RN进行了升级)1-2+项目技术分解

  2. 【bzoj1016】[JSOI2008]最小生成树计数

    1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 4863  Solved: 1973[Submit][St ...

  3. Tensorflow学习(练习)—CPU训练模型

    Mask R-CNN - Train on Shapes Dataset This notebook shows how to train Mask R-CNN on your own dataset ...

  4. 434. Number of Segments in a String 字符串中的单词个数

    [抄题]: Count the number of segments in a string, where a segment is defined to be a contiguous sequen ...

  5. 获取表中唯一字符串uuid,可用于随机文件名

    在mysql数据库中,可以使用uuid()语句来生成一个UUID:例如:mysql> select uuid();+--------------------------------------+ ...

  6. VirtualBox安装增强功能(Linux)

    我们在安装之前,必须得先安装好它所需要的依赖包,不然安装过程必定会出现错误! 一.安装依赖包 #yum install kernel-headers #yum install kernel-devel ...

  7. Java Java7处理异常新特性

  8. Python htmlTestRunner生成测试报告Demo

    #该代码段是ReadTxt_demo.py 的代码,用户读取txt 文件中的用户信息. #ReadTxt_demo.py def readTxt(filePath): fo = open(filePa ...

  9. FreeMarker的实例通俗理解

    1.把包lib/freemarker.jar拷贝到项目中 2.  在WEB-INF下新建文件夹templates  在templates下新建test.ftl文件 内容为: <html> ...

  10. How do I create a .pyc file?

    Python automatically compiles your script to compiled code, so called byte code, before running it. ...